package com.dungeon.game;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import android.graphics.Color;

import com.dungeon.game.Constants.Character_action_code;
import com.dungeon.game.Constants.Class_code;
import com.dungeon.game.Constants.Monster_action_code;
import com.dungeon.game.Constants.Monster_code;
import com.dungeon.game.Constants.Position_code;
import com.dungeon.game.Constants.Stance_code;
import com.dungeon.game.Constants.Status_effect_code;
import com.dungeon.game.Enemies.Mob;
import com.dungeon.game.Party.Character;

public class Monster_action_factory {
	Monster_action make_action(Monster_action_code ac){
        if (ac==Monster_action_code.MA_ANTICASTER){
            return new Anticaster_MA();}
        else if (ac==Monster_action_code.MA_AURA_COOL){
            return new Aura_Cool_MA();}
        else if (ac==Monster_action_code.MA_AURA_COLD){
            return new Aura_Cold_MA();}
        else if (ac==Monster_action_code.MA_AURA_FIRE){
            return new Aura_Fire_MA();}
        else if (ac==Monster_action_code.MA_AURA_FIRE_RAGE){
            return new Aura_Fire_Rage_MA();}
        else if (ac==Monster_action_code.MA_BLOCK){
            return new Block_MA();}
        else if (ac==Monster_action_code.MA_BREATH_FIRE){
            return new Breath_Fire_MA();}
        else if (ac==Monster_action_code.MA_SNIPE_DARK_ELF_BOSS){
            return new Snipe_Dark_Elf_Boss_MA();}
        else if (ac==Monster_action_code.MA_CLAW_OFTEN){
            return new Claw_Often_MA();}
        else if (ac==Monster_action_code.MA_BITE){
            return new Bite_MA();}
        else if (ac==Monster_action_code.MA_BREATH_POISON_CLOUD){
            return new Breath_Poison_Cloud_MA();}
        else if (ac==Monster_action_code.MA_LEAP_AWAY){
            return new Leap_Away_MA();}
        else if (ac==Monster_action_code.MA_WASTE_TIME){
        	return new Waste_time_MA();}
        else if (ac==Monster_action_code.MA_GAZE_SCARE_ALL){
            return new Gaze_Scare_All_MA();}
        else if (ac==Monster_action_code.MA_CATCH){
            return new Catch_MA();}
        else if (ac==Monster_action_code.MA_CLAW){
            return new Claw_MA();}
        else if (ac==Monster_action_code.MA_DARKNESS){
            return new Darkness_MA();}
        else if (ac==Monster_action_code.MA_DWARF_SPIRITS){
            return new Dwarf_Spirits_MA();}
        else if (ac==Monster_action_code.MA_ENTER_SHELL){
            return new Enter_Shell_MA();}
        else if (ac==Monster_action_code.MA_EXIT_SHELL){
            return new Exit_Shell_MA();}
        else if (ac==Monster_action_code.MA_EXPLODE){
            return new Explode_MA();}
        else if (ac==Monster_action_code.MA_FINISHING_BLOW){
            return new Finishing_Blow_MA();}
        else if (ac==Monster_action_code.MA_FIREBOLT){
            return new Firebolt_MA();}
        else if (ac==Monster_action_code.MA_FLEE){
            return new Flee_MA();}
        else if (ac==Monster_action_code.MA_FLEE_FAST){
            return new Flee_Fast_MA();}
        else if (ac==Monster_action_code.MA_FLEE_SLOWLY){
            return new Flee_Slowly_MA();}
        else if (ac==Monster_action_code.MA_FLIT){
            return new Flit_MA();}
        else if (ac==Monster_action_code.MA_FLIT_TAUNT){
            return new Flit_Taunt_MA();}
        else if (ac==Monster_action_code.MA_FLY){
            return new Fly_MA();}
        else if (ac==Monster_action_code.MA_GAZE_DRAIN_BAR){
            return new Gaze_Drain_Bar_MA();}
        else if (ac==Monster_action_code.MA_GAZE_PARALYSIS){
            return new Gaze_Paralysis_MA();}
        else if (ac==Monster_action_code.MA_GAZE_PARALYSIS_OFTEN){
            return new Gaze_Paralysis_Often_MA();}
        else if (ac==Monster_action_code.MA_GAZE_SCARE){
            return new Gaze_Scare_MA();}
        else if (ac==Monster_action_code.MA_GAZE_SLEEP){
            return new Gaze_Sleep_MA();}
        else if (ac==Monster_action_code.MA_GET_CONFUSED){
            return new Get_Confused_MA();}
        else if (ac==Monster_action_code.MA_GRAPPLE){
            return new Grapple_MA();}
        else if (ac==Monster_action_code.MA_HOWL){
            return new Howl_MA();}
        else if (ac==Monster_action_code.MA_IRISH){
            return new Irish_MA();}
        else if (ac==Monster_action_code.MA_ARGOT){
            return new Argot_MA();}
        //else if (ac==Monster_action_code.MA_METEOR_SWARM){
        //    return new Meteor_Swarm_MA();}   // TO DO LATER
        else if (ac==Monster_action_code.MA_RALLY_ORCS){
            return new Rally_Orcs_MA();}
        else if (ac==Monster_action_code.MA_RALLY_TROGLODYTES){
            return new Rally_Troglodytes_MA();}
        else if (ac==Monster_action_code.MA_SCOTS){
            return new Scots_MA();}
        else if (ac==Monster_action_code.MA_BIRD_SHRIEK_SCARE){
            return new Bird_shriek_Scare_MA();}
        else if (ac==Monster_action_code.MA_SHRIEK_SCARE){
            return new Shriek_Scare_MA();}
        else if (ac==Monster_action_code.MA_SILENCE){
            return new Silence_MA();}
        else if (ac==Monster_action_code.MA_SMITE){
            return new Smite_MA();}
        else if (ac==Monster_action_code.MA_SMITE_TROLL){
            return new Smite_Troll_MA();}
        else if (ac==Monster_action_code.MA_SMITE_BLACK_KNIGHT){
            return new Smite_Black_Knight_MA();}
        else if (ac==Monster_action_code.MA_SMITE_BLACK_OGRE){
            return new Smite_Black_Ogre_MA();}
        else if (ac==Monster_action_code.MA_SMITE_DARK_ELF){
            return new Smite_Dark_Elf_MA();}
        else if (ac==Monster_action_code.MA_SMITE_DARK_PRIEST){
            return new Smite_Dark_Priest_MA();}
        else if (ac==Monster_action_code.MA_SMITE_NAGA){
            return new Smite_Naga_MA();}
        else if (ac==Monster_action_code.MA_SMITE_OGRE){
            return new Smite_Ogre_MA();}
        else if (ac==Monster_action_code.MA_SMITE_ORC_BOSS){
            return new Smite_Orc_Boss_MA();}
        else if (ac==Monster_action_code.MA_SMITE_ORC_CHAMPION){
            return new Smite_Orc_Champion_MA();}
        else if (ac==Monster_action_code.MA_SNIPE){
            return new Snipe_MA();}
        else if (ac==Monster_action_code.MA_SNIPE_MANTICORE){
            return new Snipe_Manticore_MA();}
        else if (ac==Monster_action_code.MA_SNIPE_DARK_ELF){
            return new Snipe_Dark_Elf_MA();}
        else if (ac==Monster_action_code.MA_SNIPE_NAGA){
            return new Snipe_Naga_MA();}
        else if (ac==Monster_action_code.MA_SNIPE_ORC_BOSS){
            return new Snipe_Orc_Boss_MA();}
        else if (ac==Monster_action_code.MA_SOFT_ENRAGE){
            return new Soft_Enrage_MA();}
        else if (ac==Monster_action_code.MA_SPELL_CAUSE_WOUNDS){
            return new Spell_Cause_Wounds_MA();}
        else if (ac==Monster_action_code.MA_SPELL_DEFLECT_MISSILES){
            return new Spell_Deflect_Missiles_MA();}
        else if (ac==Monster_action_code.MA_SPELL_FIREBOLT){
            return new Spell_Firebolt_MA();}
        else if (ac==Monster_action_code.MA_SPELL_HEAL){
            return new Spell_Heal_MA();}
        else if (ac==Monster_action_code.MA_SPELL_INVULNERABILITY){
            return new Spell_Invulnerability_MA();}
        else if (ac==Monster_action_code.MA_SPELL_LIGHTNING){
            return new Spell_Lightning_MA();}
        else if (ac==Monster_action_code.MA_SPELL_PARALYSIS){
            return new Spell_Paralysis_MA();}
        else if (ac==Monster_action_code.MA_SPELL_POISON_CLOUD){
            return new Spell_Poison_Cloud_MA();}
        else if (ac==Monster_action_code.MA_SPELL_SCARE){
            return new Spell_Scare_MA();}
        else if (ac==Monster_action_code.MA_SPELL_SILENCE){
            return new Spell_Silence_MA();}
        else if (ac==Monster_action_code.MA_SPELL_SLEEP){
            return new Spell_Sleep_MA();}
        else if (ac==Monster_action_code.MA_SPIT){
            return new Spit_MA();}
        else if (ac==Monster_action_code.MA_SQUEEZE){
            return new Squeeze_MA();}
        else if (ac==Monster_action_code.MA_STUN){
            return new Stun_MA();}
        else if (ac==Monster_action_code.MA_SUMMON_ANIMALS){
            return new Summon_Animals_MA();}
        else if (ac==Monster_action_code.MA_SUMMON_COBRAS){
            return new Summon_Cobras_MA();}
        else if (ac==Monster_action_code.MA_SUMMON_EVIL_TRASH){
            return new Summon_Evil_Trash_MA();}
        else if (ac==Monster_action_code.MA_SUMMON_GOLEM){
            return new Summon_Golem_MA();}
        else if (ac==Monster_action_code.MA_SUMMON_ORCS){
            return new Summon_Orcs_MA();}
        else if (ac==Monster_action_code.MA_SUMMON_REPTILES){
            return new Summon_Reptiles_MA();}
        else if (ac==Monster_action_code.MA_SUMMON_SNAKES){
            return new Summon_Snakes_MA();}
        else if (ac==Monster_action_code.MA_SUMMON_TROGLODYTES){
            return new Summon_Troglodytes_MA();}
        else if (ac==Monster_action_code.MA_SUMMON_UNDEAD){
            return new Summon_Undead_MA();}
        else if (ac==Monster_action_code.MA_SUMMON_VARIOUS){
            return new Summon_Various_MA();}
        else if (ac==Monster_action_code.MA_SUMMON_BODYGUARD){
            return new Summon_Bodyguard_MA();}
        else if (ac==Monster_action_code.MA_SUMMON_WRETCH){
            return new Summon_Wretch_MA();}
        else if (ac==Monster_action_code.MA_SUPER_SMITE){
            return new Super_Smite_MA();}
        else if (ac==Monster_action_code.MA_SWOOP){
            return new Swoop_MA();}
        else if (ac==Monster_action_code.MA_TAILSWIPE){
            return new TailSwipe_MA();}
        else if (ac==Monster_action_code.MA_ZAP){
            return new Zap_MA();}
        else if (ac==Monster_action_code.MA_HURL_AND_ADVANCE){
            return new Hurl_And_Advance_MA();}
        else if (ac==Monster_action_code.MA_ADVANCE){
            return new Advance_MA();}
        else if (ac==Monster_action_code.MA_CHILL_DART){
            return new Spell_Chill_Dart_MA();}
        else if (ac==Monster_action_code.MA_SHOVE){
            return new Shove_MA();}
        else if (ac==Monster_action_code.MA_SHADOW_BOLT){
            return new Spell_Shadow_Bolt_MA();}
        else if (ac==Monster_action_code.MA_SUMMON_FRONT){
            return new Summon_Front_MA();}
        else if (ac==Monster_action_code.MA_CHARGE){
            return new Charge_MA();}
        else if (ac==Monster_action_code.MA_STEAL){
            return new Steal_MA();}
        else if (ac==Monster_action_code.MA_SUMMON_LOW_UNDEAD){
            return new Summon_Low_Undead_MA();}
        else if (ac==Monster_action_code.MA_STING_PARALYSIS){
            return new Sting_Paralysis_MA();}
        else if (ac==Monster_action_code.MA_BREATH_SMALL_FIRE){
            return new Breath_Small_Fire_MA();}
        else if (ac==Monster_action_code.MA_BURNS){
            return new Burns_MA();}
        else if (ac==Monster_action_code.MA_CURSE){
            return new Curse_MA();}
        else if (ac==Monster_action_code.MA_TORNADO){
            return new Tornado_MA();}
        else if (ac==Monster_action_code.MA_SNIPE_POLTERGEIST){
            return new Snipe_Poltergeist_MA();}
        else if (ac==Monster_action_code.MA_CLEAVE){
            return new Cleave_MA();}
        else if (ac==Monster_action_code.MA_SMITE_BERSERKER){
            return new Smite_Berserker_MA();}
        else if (ac==Monster_action_code.MA_SQUARK) return new Squark_MA();
        else if (ac==Monster_action_code.MA_BACK_ATTACK) return new Back_attack_MA();
        else if (ac==Monster_action_code.MA_BACK_ATTACK_2) return new Back_attack_2_MA();
        else if (ac==Monster_action_code.MA_SMALL_CLAW) return new Small_claw_MA();
        else if (ac==Monster_action_code.MA_TORNADO_DISAPPEAR) return new Tornado_disappear_MA();
        else if (ac==Monster_action_code.MA_LUNGE_1) return new Lunge_1_MA();
        else if (ac==Monster_action_code.MA_DISPEL_MAGIC) return new Dispel_magic_MA();
        else if (ac==Monster_action_code.MA_DARKNESS_OFTEN) return new Darkness_often_MA();
        else if (ac==Monster_action_code.MA_DEBUFF_DISAPPEAR) return new Debuff_disappear_MA();
        else if (ac==Monster_action_code.MA_BREATH_MINI_FIRE) return new Breath_mini_fire_MA();
        else if (ac==Monster_action_code.MA_SUMMON_MINOR_DEMONS) return new Summon_minor_demons_MA();
        else if (ac==Monster_action_code.MA_SUMMON_MOUNTAIN_ANIMALS) return new Summon_mountain_animals_MA();
        else if (ac==Monster_action_code.MA_SUMMON_DOPPELGANGER) return new Summon_doppelganger_MA();
        else if (ac==Monster_action_code.MA_BREATH_MINI_LIGHTNING) return new Breath_mini_lightning_MA();
        else if (ac==Monster_action_code.MA_BREATH_SMALL_LIGHTNING) return new Breath_small_lightning_MA();
        else if (ac==Monster_action_code.MA_COUNTER_HEALING) return new Counter_healing_MA();
        else if (ac==Monster_action_code.MA_COUNTER_WARD) return new Counter_ward_MA();
        else if (ac==Monster_action_code.MA_REFLECTION) return new Reflection_MA();
        else if (ac==Monster_action_code.MA_FOCUS) return new Focus_MA();
        else if (ac==Monster_action_code.MA_DRAINING_TOUCH_DISAPPEAR) return new Draining_touch_disappear_MA();
        else if (ac==Monster_action_code.MA_GLOAT) return new Gloat_MA();
        else if (ac==Monster_action_code.MA_BREATH_MINI_POISON) return new Breath_mini_poison_MA();
        else if (ac==Monster_action_code.MA_BREATH_MINI_COLD) return new Breath_mini_cold_MA();
        else if (ac==Monster_action_code.MA_ICE_STORM) return new Ice_storm_MA();
        else if (ac==Monster_action_code.MA_SPELL_FIREBALL) return new Spell_fireball_MA();
        else if (ac==Monster_action_code.MA_STRANGLING_WEBS) return new Strangling_webs_MA();
        else if (ac==Monster_action_code.MA_FLURRY) return new Flurry_MA();
        else if (ac==Monster_action_code.MA_FLURRY_CHEST) return new Flurry_Chest_MA();
        else if (ac==Monster_action_code.MA_SMALL_FLURRY) return new Small_flurry_MA();
        else if (ac==Monster_action_code.MA_DROP_BOULDER) return new Drop_boulder_MA();
        else if (ac==Monster_action_code.MA_IMITATE) return new Imitate_MA();
        else if (ac==Monster_action_code.MA_SUMMON_ILLUSION) return new Summon_illusion_MA();
        else if (ac==Monster_action_code.MA_SUMMON_ILLUSION_OFTEN) return new Summon_illusion_often_MA();
        else if (ac==Monster_action_code.MA_MASS_HEALING) return new Mass_healing_MA();
        else if (ac==Monster_action_code.MA_GAZE_PETRIFY) return new Gaze_petrify_MA();
        else if (ac==Monster_action_code.MA_STAB_AND_DISAPPEAR) return new Stab_and_disappear_MA();
        else if (ac==Monster_action_code.MA_SNIPE_SNIPER) return new Snipe_sniper_MA();
        else if (ac==Monster_action_code.MA_ANGST) return new Angst_MA();
        else if (ac==Monster_action_code.MA_EARTH_SHOCK) return new Earth_shock_MA();
        else if (ac==Monster_action_code.MA_DROWN) return new Drown_MA();
        else if (ac==Monster_action_code.MA_DISAPPEAR) return new Disappear_MA();
        else if (ac==Monster_action_code.MA_SMITE_OATHWRAITH) return new Smite_Oathwraith_MA();
        else if (ac==Monster_action_code.MA_FINGER_OF_DEATH) return new Finger_of_death_MA();
        else if (ac==Monster_action_code.MA_DARKNESS_STORM) return new Darkness_storm_MA();
        else if (ac==Monster_action_code.MA_BIG_SHADOW_BOLT) return new Big_shadow_bolt_MA();
        else if (ac==Monster_action_code.MA_DRAINING_TOUCH) return new Draining_touch_MA();
        else if (ac==Monster_action_code.MA_BREATH_LIGHTNING) return new Breath_lightning_MA();
        else if (ac==Monster_action_code.MA_CLAW_FAST_AND_OFTEN) return new Claw_fast_and_often_MA();
        else if (ac==Monster_action_code.MA_FLASHING_GAZE) return new Flashing_gaze_MA();
        else if (ac==Monster_action_code.MA_REAR_UP) return new Rear_up_MA();
        else if (ac==Monster_action_code.MA_METEOR_SWARM_WAIT_SUMMON_UNDEADS_FLEE) return new Meteor_swarm_wait_summon_undeads_flee_MA();
        else if (ac==Monster_action_code.MA_ALWAYS_FLY) return new Always_fly_MA();
        else if (ac==Monster_action_code.MA_ALWAYS_FLY_AND_LEAVE_LATER) return new Always_fly_and_leave_later_MA();
        else if (ac==Monster_action_code.MA_SPELL_BIG_DRAIN) return new Spell_big_drain_MA();
        else if (ac==Monster_action_code.MA_SUMMON_GOLEMS_INSTANTLY_AND_METEOR_SWARM_LATER) return new Summon_golems_instantly_and_meteor_swarm_later_MA();
        else if (ac==Monster_action_code.MA_SMITE_ORC_BOSS_2) return new Smite_orc_boss_2_MA();
        else if (ac==Monster_action_code.MA_LUNGE_2) return new Lunge_2_MA();
        else if (ac==Monster_action_code.MA_SUMMON_WOLVES) return new Summon_wolves_MA();
        else if (ac==Monster_action_code.MA_SUMMON_WHITE_WOLVES) return new Summon_white_wolves_MA();
        else if (ac==Monster_action_code.MA_EPIC_BLOCK) return new Epic_block_MA();
        else if (ac==Monster_action_code.MA_SUPER_SMITE_OFTEN) return new Super_smite_often_MA();
        else if (ac==Monster_action_code.MA_HURL) return new Hurl_MA();
        else if (ac==Monster_action_code.MA_LICH_WASTE_TIME) return new Lich_waste_time_MA();
        else if (ac==Monster_action_code.MA_FLY_OFTEN) return new Fly_Often_MA();
        else if (ac==Monster_action_code.MA_FLIT_OFTEN) return new Flit_Often_MA();
        return null;
	}
}

class Monster_action implements Serializable {

	Mob m;
	Monster_action_code action_code;
	Double last_used;
	Mob mob_target;
	Character char_target;
	Integer n_times_used;

	Monster_action(){
		last_used = null;
		n_times_used = 0;
	}

	String name(){
		return "";
	}

	String message(){
		return null;
	}

    String warning_message(){
        return "";
    }

    boolean target_friend(){
        return false;
    }

    boolean target_enemy(){
        return false;
    }

    boolean enemy_target_back_row_ok(){
        return true; // but usually suppressed by target_enemy=false
    }

    boolean enemy_target_front_row_ok(){
        return true; // but usually suppressed by target_enemy=false
    }

    Double period(){
        return null;
    }

    Double likelihood_increase_until_n_uses(){
        return 1.0;
    }

    Integer n_uses(){
        return 1;
    }

    Double delay(){
        return 0.0;
    }

    Double cooldown(){
        return null;
    }

    Double gc(){
        return null;
    }

    boolean back_row_ok(){
        return true;
    }

    boolean front_row_ok(){
        return true;
    }

    boolean ok(){
        if (m.front_row() && !front_row_ok())
            return false;
        if (!m.front_row() && !back_row_ok())
            return false;
        if (target_enemy() && char_valid_targets().size()==0)
            return false;
        if (target_friend() && mob_valid_targets().size()==0)
            return false;
        if (cooldown()!=null && last_used!=null && last_used+cooldown()>Gamestate.w.clock)
            return false;
        if (m.waste_time_until > Gamestate.w.clock)
        	return false;
        if (m.show_off_until > Gamestate.w.clock)
        	return false;
        return true;
    }

    boolean friend_target_ok(Mob mm){
        if (m.id==mm.id)
            return false;
        if (!mm.alive)
            return false;
        if (mm.about_to_die)
            return false;
        return true;
    }

    boolean enemy_target_ok(Character c){
        if (!c.alive)
            return false;
        if (c.about_to_die)
            return false;
        if (c.front_row() && !enemy_target_front_row_ok())
            return false;
        if (!c.front_row() && !enemy_target_back_row_ok())
            return false;
        return true;
    }

    List<Mob> mob_valid_targets(){
        if (target_friend()){
        	ArrayList<Mob> targets = new ArrayList<Mob>();
            for (Mob mm : Gamestate.e.mobs){
                if (friend_target_ok(mm))
                    targets.add(mm);
            }
            return targets;
        } else {
            return new ArrayList<Mob>();
        }
    }

    List<Character> char_valid_targets(){
        if (target_enemy()){
        	ArrayList<Character> targets = new ArrayList<Character>();
            for (Character c : Gamestate.p.chars){
                if (enemy_target_ok(c))
                    targets.add(c);
            }
            return targets;
        } else {
            return new ArrayList<Character>();
        }
    }

    void trigger(){
        if (delay()>0){
            m.action_in_progress = this;
            m.action_fires = Gamestate.w.clock + delay();
            if (!warning_message().equals(""))
            	Gamestate.add_message(warning_message(),m.image,Color.RED);
        } else {
            resolve();
        }
        last_used = Gamestate.w.clock;
        n_times_used = n_times_used + 1;
        if (gc()!=null)
            m.gc_until = Gamestate.w.clock + gc();
    }

    void resolve(){
        if (prevented()){
            Gamestate.add_message("Blocked",m.image,Color.WHITE);
        } else if ((target_friend() && !friend_target_ok(mob_target)) || (target_enemy() && !enemy_target_ok(char_target))){
        	Gamestate.add_message("Action failed",m.image,Color.WHITE);
        } else {
            m.flash_image = "verypale";
            String text = name();
            if (message()!=null) text=message();
            if (!text.equals("")){
                if (char_target!=null){
                	Gamestate.add_message(text + " [" + char_target.name + "]",m.image,Color.WHITE);
                } else if (mob_target!=null){
                	Gamestate.add_message(text + " [" + mob_target.name + "]",m.image,Color.WHITE);
                } else {
                	Gamestate.add_message(text,m.image,Color.WHITE);
                }
            }
            make_stuff_happen();
        }
    }

    void make_stuff_happen(){
        // stub;
    }

    boolean prevented(){
        return false;
    }

    String get_description(){
        return "";
    }

    boolean disruptable(){
        return false;
    }

    boolean blockable(){
        return false;
    }

    boolean semi_blockable(){
        return false;
    }

    boolean deflectable(){
        return false;
    }

    boolean fiery(){
        return false;
    }

    boolean freezy(){
        return false;
    }

    boolean dark(){
        return false;
    }

    boolean draining(){
        return false;
    }

    boolean paralysing(){
        return false;
    }

    boolean scary(){
        return false;
    }

    boolean sleepy(){
        return false;
    }

    boolean bolt(){
        return false;
    }

    boolean finishing(){
        return false;
    }

    boolean silencing(){
        return false;
    }

    boolean epic_blockable(){
        return false;
    }

    boolean leap_awayable(){
        return false;
    }

    boolean take_coverable(){
        return false;
    }

    static int success_chance(int x){
        if (x>=12)
            return 100;
        if (x==11)
            return 99;
        if (x==10)
            return 97;
        if (x==9)
            return 95;
        if (x==8)
            return 90;
        if (x==7)
            return 70;
        if (x==6)
            return 50;
        if (x==5)
            return 30;
        if (x==4)
            return 10;
        if (x==3)
            return 5;
        if (x==2)
            return 3;
        if (x==1)
            return 1;
        return 0;
    }

    void dispel(Character c){
    	for (Status_effect_code sec : Arrays.asList(Status_effect_code.EF_SPEED,Status_effect_code.EF_DODGE,Status_effect_code.EF_RESIST_FIRE,Status_effect_code.EF_RESIST_POISON,Status_effect_code.EF_TAUNT,Status_effect_code.EF_RIGHTEOUSNESS,Status_effect_code.EF_FRONTROWBUFF,Status_effect_code.EF_INSULT,Status_effect_code.EF_FIND_WEAKNESS,Status_effect_code.EF_FORCEBRAND,Status_effect_code.EF_EV,Status_effect_code.EF_SHIELD,Status_effect_code.EF_MEDITATION,Status_effect_code.EF_RESIST_COLD,Status_effect_code.EF_IMMUNE_FIRE,Status_effect_code.EF_ACCURATE,Status_effect_code.EF_INVULNERABLE,Status_effect_code.EF_BATTLECRY,Status_effect_code.EF_REFLEXES,Status_effect_code.EF_RAPIDITY,Status_effect_code.EF_RECKLESS,Status_effect_code.EF_BLESSING,Status_effect_code.EF_LIFE_LEECH)){
    		if (c.has_status_effect(sec)){
    			c.status_effects.get(sec).lasts_until = 0;
    		}
    	}
    	for (Stance_code sc : Arrays.asList(Stance_code.ST_RETRENCH,Stance_code.ST_ALL_OUT_DEFENSE,Stance_code.ST_BERSERK,Stance_code.ST_INVULNERABLE)){
    		if (c.current_stance!=null && c.current_stance==sc){
    			c.current_stance = null;
    		}
    	}
   }
}

class Rear_up_MA extends Monster_action {
    public Rear_up_MA(){
        super();
    }

    String name(){
        return "Rear Up";
    }

    String message(){
        return "Rears onto its hind legs to show off its belly!";
    }

    Double period(){
    	return 15.0;
    }

    Double cooldown(){
    	return 35.0;
    }

    boolean front_row_ok(){
        return true;
    }

    boolean back_row_ok(){
        return false;
    }

    String get_description(){
        return "The proud dragon likes to show off its beautiful scales!";
    }

    void make_stuff_happen(){
    	m.show_off_until = Gamestate.w.clock + 5.0;
    }
}

class Waste_time_MA extends Monster_action {
    public Waste_time_MA(){
        super();
    }

    String name(){
        return "Waste Time";
    }

    String message(){
        return "Folds his arms and watches you";
    }

    Double period(){
    	return 5.0;
    }

    Double cooldown(){
    	return 15.0;
    }

    boolean front_row_ok(){
        return false;
    }

    String get_description(){
        return "Sometimes he just likes to stand back and watch the chaos that ensues.";
    }

    void make_stuff_happen(){
    	m.waste_time_until = Gamestate.w.clock + 5.0;
    }
}

class Lich_waste_time_MA extends Waste_time_MA {
    public Lich_waste_time_MA(){
        super();
    }

    String name(){
        return "Retrench";
    }

    String message(){
        return "Gathers his power...";
    }

    Double cooldown(){
    	return 10.0;
    }

    String get_description(){
        return "The undead sorcerer is starting to run low on magical energy.";
    }
}

class Drown_MA extends Monster_action {
	public Drown_MA(){
	    super();
	}

	String name(){
	    return "Drown";
	}

	Double period(){
		return 5.0;
	}

	Double cooldown(){
		return 20.0;
	}

	boolean target_enemy(){
		return true;
	}

    boolean enemy_target_ok(Character c){
        if (c.drowning) return false;
        return super.enemy_target_ok(c);
    }

	void make_stuff_happen(){
        Gamestate.add_sound("extinguish");
		char_target.drowning = true;
		char_target.drowning_from = Gamestate.w.clock;
	}

	String get_description(){
	    return "It can drown you, if you don't kill it quickly...";
	}
}

class Disappear_MA extends Monster_action {
    public Disappear_MA(){
        super();
    }

    String name(){
        return "Submerge";
    }

    Double period(){
    	return 20.0;
    }

    void make_stuff_happen(){
		Gamestate.add_sound("submerge");
    	Gamestate.w.earth_elemental_gone = true;
    	Gamestate.w.earth_elemental_returns = Gamestate.w.clock + Gamestate.w.randint(4,6);
    	Gamestate.w.earth_elemental_health = m.health;
        m.remove();
	}

	String get_description(){
	    return "It can disappear under the ground, and reappear again somewhere else.";
	}
}

class Imitate_MA extends Monster_action {
    public Imitate_MA(){
        super();
    }

    String name(){
        return "Imitate";
    }

    boolean target_enemy(){
        return true;
    }

    List<Character> char_valid_targets(){
    	List<Character> candidates = super.char_valid_targets();
    	ArrayList<Character> keepers = new ArrayList<Character>();
    	for (Character c : candidates){
    		boolean c_ok = true;
    		for (Mob m2 : Gamestate.e.mobs){
    			if (m2.alive && !m2.about_to_die && m2.image==c.image){
    				c_ok = false;
    			}
    		}
    		if (c_ok) keepers.add(c);
    	}
    	return keepers;
    }

    Double period(){
    	return 2.0;
    }

    void make_stuff_happen(){
		Gamestate.add_sound("cackle");
    	m.max_health = (int)Math.ceil(char_target.max_health * (int)Math.max(1,(1 + (char_target.max_health-40) / 40)));
    	m.health = m.max_health;
    	m.image = char_target.image;
    	m.action_in_progress = null;
    	if (char_target.melee_attack){
    		m.melee_attack = true;
    		m.melee_period = char_target.current_melee_period()*0.9;
    		m.accuracy = char_target.current_accuracy()+1;
    		m.melee_hit_sound = char_target.melee_hit_sound;
    		m.melee_miss_sound = char_target.melee_miss_sound;
    		m.melee_min_dam = (int)Math.ceil((char_target.melee_dice) * (1.0 + 0.1*char_target.current_damage()) * 1.3);
    		m.melee_max_dam = (int)Math.ceil((char_target.melee_dice * char_target.melee_sides) * (1.0 + 0.1*char_target.current_damage()) * 1.3);
    	} else {
    		m.melee_attack = false;
    	}
    	if (char_target.missile_attack){
    		m.missile_attack = true;
    		m.missile_period = char_target.current_missile_period()*0.9;
    		m.aim = char_target.current_aim()+1;
    		m.missile_hit_sound = char_target.missile_hit_sound;
    		m.missile_miss_sound = char_target.missile_miss_sound;
    		m.missile_min_dam = (int)Math.ceil((char_target.missile_dice) * (1.0 + 0.1*char_target.current_damage()) * 1.3);
    		m.missile_max_dam = (int)Math.ceil((char_target.missile_dice * char_target.missile_sides) * (1.0 + 0.1*char_target.current_penetration()) * 1.3);
    	} else {
    		m.missile_attack = false;
    	}
    	m.evasion = char_target.current_evasion();
    	m.armor = char_target.current_armor()*3/2;
    	m.prefer_back = (char_target.class_code==Class_code.C_MAGE || char_target.class_code==Class_code.C_ROGUE);
    	m.poison_resistant = char_target.poison_resistance;
    	m.fire_resistant = char_target.fire_resistance;
    	m.cold_resistant = char_target.cold_resistance;
    	m.actions = new ArrayList<Monster_action>();
    	for (Character_action ca : char_target.actions){
    		if (ca.action_code==Character_action_code.CA_BASH || ca.action_code==Character_action_code.CA_SMITE)
    			m.add_action(Monster_action_code.MA_SMITE_NAGA);
    		if (ca.action_code==Character_action_code.CA_FLURRY)
    			m.add_action(Monster_action_code.MA_SMALL_FLURRY);
    		if (ca.action_code==Character_action_code.CA_BIG_HEAL || ca.action_code==Character_action_code.CA_SMALL_HEAL || ca.action_code==Character_action_code.CA_HEALING || ca.action_code==Character_action_code.CA_KNIT_FLESH)
    			m.add_action(Monster_action_code.MA_SPELL_HEAL);
    		if (ca.action_code==Character_action_code.CA_BLOCK || ca.action_code==Character_action_code.CA_RIPOSTE)
    			m.add_action(Monster_action_code.MA_BLOCK);
    		if (ca.action_code==Character_action_code.CA_LIGHTNING_BOLT)
    			m.add_action(Monster_action_code.MA_SPELL_LIGHTNING);
    		if (ca.action_code==Character_action_code.CA_FIREBOLT)
    			m.add_action(Monster_action_code.MA_SPELL_FIREBOLT);
    		if (ca.action_code==Character_action_code.CA_FIREBALL)
    			m.add_action(Monster_action_code.MA_SPELL_FIREBALL);
    		if (ca.action_code==Character_action_code.CA_ICE_STORM)
    			m.add_action(Monster_action_code.MA_ICE_STORM);
    		if (ca.action_code==Character_action_code.CA_CLEAVE || ca.action_code==Character_action_code.CA_SWIPE)
    			m.add_action(Monster_action_code.MA_CLEAVE);
    		if (ca.action_code==Character_action_code.CA_COUP_DE_GRACE || ca.action_code==Character_action_code.CA_DEATHBLOW)
    			m.add_action(Monster_action_code.MA_FINISHING_BLOW);
    		if (ca.action_code==Character_action_code.CA_DEADLY_BLADE || ca.action_code==Character_action_code.CA_WEAKEN)
    			m.poison_melee = 2;
    		if (ca.action_code==Character_action_code.CA_FEAR)
    			m.add_action(Monster_action_code.MA_SPELL_SCARE);
    		if (ca.action_code==Character_action_code.CA_INVULNERABILITY)
    			m.add_action(Monster_action_code.MA_SPELL_INVULNERABILITY);
    		if (ca.action_code==Character_action_code.CA_POISON_CLOUD || ca.action_code==Character_action_code.CA_POISON_CLOUD_2)
    			m.add_action(Monster_action_code.MA_SPELL_POISON_CLOUD);
    		if (ca.action_code==Character_action_code.CA_KNOCKBACK)
    			m.add_action(Monster_action_code.MA_SHOVE);
    		if (ca.action_code==Character_action_code.CA_STUNNING_BLOW)
    			m.add_action(Monster_action_code.MA_STUN);
    		if (ca.action_code==Character_action_code.CA_LOB || ca.action_code==Character_action_code.CA_SNIPE)
    			m.add_action(Monster_action_code.MA_SNIPE_NAGA);
    		if (ca.action_code==Character_action_code.CA_POLEARM_STRIKE)
    			m.add_action(Monster_action_code.MA_BACK_ATTACK);
    		if (ca.action_code==Character_action_code.CA_REFLECTION)
    			m.add_action(Monster_action_code.MA_REFLECTION);
    	}
    }

    String get_description(){
        return "It can turn itself into a clone of a member of your party.";
    }
}

class Drop_boulder_MA extends Monster_action {
    public Drop_boulder_MA(){
        super();
    }

    String name(){
        return "Drop a Boulder";
    }

    Double likelihood_increase_until_n_uses(){
        return 3.0;
    }

    Integer n_uses(){
        return 1;
    }

    Double period(){
        return 12.0;
    }

    Double cooldown(){
    	return 6.0;
    }

    Double gc(){
    	return 4.0;
    }

    boolean back_row_ok(){
        return true;
    }

    boolean front_row_ok(){
        return false;
    }

    void make_stuff_happen(){
    	Character tgt = Gamestate.p.random_target(m);
    	if (tgt!=null){
    		Gamestate.w.boulder_pos = tgt.pos;
    		if (Gamestate.w.boulder_pos!=null)
    			Gamestate.w.boulder_dropped = Gamestate.w.clock + 1.0;
    	}
    }

    String get_description(){
        return "When it is at long range, it can pick up a heavy boulder and drop it on your party - better not to be standing under it when it lands! One defense is for the character standing on Ground Zero to 'Leap Aside'.";
    }
}

class Flurry_smite_MA extends Smite_MA {
	// not created in the normal way
    public Flurry_smite_MA(){
        super();
    }

    String name(){
        return "Slash!";
    }

    String warning_message(){
        return "";
    }

    int min_damage(){
        return 10;
    }

    int max_damage(){
        return 20;
    }

    Double delay(){
        return 1.5;
    }

    String sound(){
    	return "sword";}
}

class Small_flurry_smite_MA extends Flurry_smite_MA {
	// not created in the normal way
    public Small_flurry_smite_MA(){
        super();
    }

    int min_damage(){
        return 5;
    }

    int max_damage(){
        return 10;
    }
}

class Chest_flurry_smite_MA extends Flurry_smite_MA {
	// not created in the normal way
    public Chest_flurry_smite_MA(){
        super();
    }

    String name(){
        return "Slam!";
    }

    String warning_message(){
        return "";
    }

    int min_damage(){
        return 10;
    }

    int max_damage(){
        return 20;
    }

    Double delay(){
        return 1.5;
    }

    String sound(){
    	return "bite";}
}

class Flurry_Chest_MA extends Small_flurry_MA{
    public Flurry_Chest_MA(){
        super();
    }

    void make_stuff_happen(){
    	m.n_chest_flurries_remaining = 3;
    }
}

class Small_flurry_MA extends Flurry_MA {
    public Small_flurry_MA(){
        super();
    }

    void make_stuff_happen(){
    	m.n_small_flurries_remaining = 3;
    }
}

class Flurry_MA extends Monster_action {
    public Flurry_MA(){
        super();
    }

    String name(){
        return "Flurry";
    }

    Double period(){
        return 5.0;
    }

    Double cooldown(){
    	return 5.0;
    }

    Double gc(){
    	return 4.0;
    }

    boolean back_row_ok(){
        return false;
    }

    void make_stuff_happen(){
    	m.n_flurries_remaining = 3;
    }

    String get_description(){
        return "Launches into three Smite attacks in rapid succession.";
    }
}

class Strangling_webs_MA extends Monster_action {
    public Strangling_webs_MA(){
        super();
    }

    String name(){
        return "Strangling Webs";
    }

    Double period(){
        return 5.0;
    }

    Double cooldown(){
    	return 5.0;
    }

    Double gc(){
    	return 1.0;
    }

    boolean back_row_ok(){
        return false;
    }

    boolean target_enemy(){
        return true;
    }

    int duration(){
    	return Gamestate.w.randint(5,10);
    }

    void make_stuff_happen(){
        if (char_target.alive && !char_target.about_to_die){
            boolean immune = false;
            for (Character_action a : char_target.actions){
                if (a.action_code==Character_action_code.CA_FREE_ACTION){
                    immune = true;
                    continue;
                }
            }
            if (immune){
                Gamestate.add_message("Failed to catch " + char_target.name,m.image,Color.WHITE);
            } else {
    	        Gamestate.add_sound("grunt");
    	        int d = duration();
            	char_target.status_effects.put(Status_effect_code.EF_PARALYSIS,new Effect_parameters(d));
            	char_target.webbed_until = Gamestate.w.clock + d;
            	char_target.action_in_progress = null;
            	char_target.current_stance = null;
            }
        }
    }

    String get_description(){
        return "Casts a web at one of your characters, entangling and incapacitating them for a few seconds.";
    }
}

class Spell_fireball_MA extends Monster_Spell_MA {
    public Spell_fireball_MA(){
        super();
    }

    String name(){
        return "Fireball";
    }

    Double likelihood_increase_until_n_uses(){
        return 3.0;
    }

    Integer n_uses(){
        return 2;
    }

    boolean fiery(){
        return true;
    }

    boolean take_coverable(){
        return true;
    }

    int damroll(){
        return Gamestate.w.randint(20,40);
    }

    void make_stuff_happen(){
       Gamestate.add_sound("breath");
       for (Character c : Gamestate.p.chars){
           if (c.alive && !c.about_to_die){
               int damage = damroll();
               if (c.current_stance==Stance_code.ST_TAKE_COVER){
                   damage = damage / 3;
                   c.current_stance = null;
               }
        	   if (c.has_status_effect(Status_effect_code.EF_IMMUNE_FIRE))
                   damage = damage / 4;
               else if (c.has_status_effect(Status_effect_code.EF_RESIST_FIRE) || c.fire_resistance)
                   damage = damage / 2;
               c.take_damage(damage);
               c.flash_image = "fire_trap";
               c.flash_long = true;
           }
       }
    }

    String get_description(){
        return "Makes a ball of fire explode among your party. Can be countered by Take Cover, though you will still take some damage.";
    }
}

class Ice_storm_MA extends Monster_Spell_MA {
    public Ice_storm_MA(){
        super();
    }

    String name(){
        return "Ice Storm";
    }

    Double likelihood_increase_until_n_uses(){
        return 3.0;
    }

    Integer n_uses(){
        return 2;
    }

    boolean freezy(){
        return true;
    }

    boolean take_coverable(){
        return true;
    }

    int damroll(){
        return Gamestate.w.randint(10,30);
    }

    void make_stuff_happen(){
       Gamestate.add_sound("wind");
       for (Character c : Gamestate.p.chars){
           if (c.alive && !c.about_to_die){
               int damage = damroll();
               if (c.current_stance==Stance_code.ST_TAKE_COVER){
                   damage = damage / 3;
                   c.current_stance = null;
               } else {
            	   if (c.has_status_effect(Status_effect_code.EF_RESIST_COLD) || c.cold_resistance)
                       damage = damage / 4;
               }
               c.take_damage(damage);
           }
       }
    }

    String get_description(){
        return "Makes a freezing blizzard sweep across your party. Can be countered by Take Cover, though you will still take some damage. Cold resistance reduces the damage by 75%.";
    }
}

class Gloat_MA extends Monster_action {
    public Gloat_MA(){
        super();
    }

    String name(){
        return "Gloat";
    }

    String message(){
        return "Laughs at you";
    }

    Double period(){
        return 2.0;
    }

    Double cooldown(){
    	return 10.0;
    }

    boolean ok(){
    	if (!super.ok()) return false;
    	return Gamestate.w.last_char_death>0 && Gamestate.w.last_char_death>Gamestate.w.clock-5.0;
    }

    void make_stuff_happen(){
		Gamestate.add_sound("evil laugh");
    	m.health = (int)Math.min(m.max_health, m.health + m.max_health*0.25);
    }

    String get_description(){
        return "This monster rejoices if it sees one of your characters killed.";
    }
}

class Focus_MA extends Monster_action {
    public Focus_MA(){
        super();
    }

    String name(){
        return "Focus";
    }
    
    String message(){
        return "";
    }

    Double period(){
        return 10.0;
    }

    Double cooldown(){
    	return 99.0;
    }

    void make_stuff_happen(){
    	for (int i=0; i<10; i++){
    		Character c = Gamestate.p.random_front_target(m);
    		if (c.alive && !c.about_to_die){
                Gamestate.add_message("Focuses its attacks on " + c.name,m.image,Color.WHITE);
    			m.focused_on = c;
    			return;
    		}
    	}
    }

    String get_description(){
        return "This enemy may concentrate on a single front-row target and direct most of their attacks against that target.";
    }
}

class Reflection_MA extends Monster_action {
    public Reflection_MA(){
        super();
    }

    String name(){
        return "Reflection";
    }

    Double period(){
        return 2.0;
    }

    Double cooldown(){
    	return 5.0;
    }

    boolean ok(){
    	if (!super.ok()) return false;
    	for (Character c : Gamestate.p.chars){
    		if (c.alive && !c.about_to_die && c.action_in_progress!=null)
    			if (c.action_in_progress.action_code==Character_action_code.CA_FIREBOLT || c.action_in_progress.action_code==Character_action_code.CA_FROST_BOLT || c.action_in_progress.action_code==Character_action_code.CA_LIGHTNING_BOLT || c.action_in_progress.action_code==Character_action_code.CA_CHAIN_LIGHTNING || c.action_in_progress.action_code==Character_action_code.CA_SPARK || c.action_in_progress.action_code==Character_action_code.CA_POISON_BOLT || c.action_in_progress.action_code==Character_action_code.CA_RAY_OF_LIGHT || c.action_in_progress.action_code==Character_action_code.CA_HOLY_LIGHT)
    				return true;
    	}
    	return false;
    }

    void make_stuff_happen(){
    	for (Character c : Gamestate.p.chars){
    		if (c.alive && !c.about_to_die && c.action_in_progress!=null)
    			if (c.action_in_progress.action_code==Character_action_code.CA_FIREBOLT || c.action_in_progress.action_code==Character_action_code.CA_FROST_BOLT || c.action_in_progress.action_code==Character_action_code.CA_LIGHTNING_BOLT || c.action_in_progress.action_code==Character_action_code.CA_CHAIN_LIGHTNING || c.action_in_progress.action_code==Character_action_code.CA_SPARK || c.action_in_progress.action_code==Character_action_code.CA_POISON_BOLT || c.action_in_progress.action_code==Character_action_code.CA_RAY_OF_LIGHT || c.action_in_progress.action_code==Character_action_code.CA_HOLY_LIGHT){
        	    	Gamestate.add_sound("fail");
    				c.action_in_progress = null;
    			}
    	}
    }

    String get_description(){
        return "This counterspell is used to block your bolt spells.";
    }
}

class Counter_ward_MA extends Monster_action {
    public Counter_ward_MA(){
        super();
    }

    String name(){
        return "Counter: Wards";
    }

    Double period(){
        return 0.5;
    }

    Double cooldown(){
    	return 5.0;
    }

    boolean ok(){
    	if (!super.ok()) return false;
    	for (Character c : Gamestate.p.chars){
    		if (c.alive && !c.about_to_die && c.current_stance!=null)
    			if (c.current_stance==Stance_code.ST_COUNTER_BOLTS || c.current_stance==Stance_code.ST_COUNTER_DARKNESS || c.current_stance==Stance_code.ST_COUNTER_DRAINING || c.current_stance==Stance_code.ST_COUNTER_FEAR || c.current_stance==Stance_code.ST_COUNTER_PARALYSIS || c.current_stance==Stance_code.ST_COUNTER_SILENCE || c.current_stance==Stance_code.ST_COUNTER_SLEEP)
    				return true;
    	}
    	return false;
    }

    void make_stuff_happen(){
    	for (Character c : Gamestate.p.chars){
    		if (c.alive && !c.about_to_die && c.current_stance!=null)
    			if (c.current_stance==Stance_code.ST_COUNTER_BOLTS || c.current_stance==Stance_code.ST_COUNTER_DARKNESS || c.current_stance==Stance_code.ST_COUNTER_DRAINING || c.current_stance==Stance_code.ST_COUNTER_FEAR || c.current_stance==Stance_code.ST_COUNTER_PARALYSIS || c.current_stance==Stance_code.ST_COUNTER_SILENCE || c.current_stance==Stance_code.ST_COUNTER_SLEEP){
        	    	Gamestate.add_sound("fail");
    				c.current_stance = null;
    			}
    	}
    }

    String get_description(){
        return "This counter-counterspell prevents your counterspells from taking effect.";
    }
}

class Counter_healing_MA extends Monster_action {
    public Counter_healing_MA(){
        super();
    }

    String name(){
        return "Counter: Healing";
    }

    Double period(){
        return 0.5;
    }

    Double cooldown(){
    	return 5.0;
    }

    boolean ok(){
    	return super.ok() && Gamestate.w.last_heal > 0 && Gamestate.w.last_heal > Gamestate.w.clock - 2.0;
    }

    void make_stuff_happen(){
    	if (Gamestate.w.last_heal_target!=null && Gamestate.w.last_heal_target.alive && !Gamestate.w.last_heal_target.about_to_die){
	    	Gamestate.add_sound("fail");
	        Gamestate.w.last_heal_target.take_damage(Gamestate.w.last_heal_amount);
	        Gamestate.w.last_heal = 0;
    	}
    }

    String get_description(){
        return "This counterspell reverses the effects of one of your healing spells.";
    }
}

class Draining_touch_disappear_MA extends Monster_action {
    public Draining_touch_disappear_MA(){
        super();
    }

    String name(){
        return "Draining Touch";
    }

    Double period(){
        return 2.0;
    }

    Double gc(){
        return 1.0;
    }

    boolean back_row_ok(){
        return false;
    }

    boolean target_enemy(){
        return true;
    }

    boolean enemy_target_front_row_ok(){
        return true;
    }

    boolean enemy_target_back_row_ok(){
        return true;
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_DRAINING){
    	    	Gamestate.add_sound("fail");
                c.current_stance = null;
                return true;
            }
        }
        return false;
    }

    int damroll(){
    	return Gamestate.w.randint(5,15);
    }

    void make_stuff_happen(){
    	if (!char_target.draining_resistance){
	        Gamestate.add_sound("exhale");
	        char_target.take_damage(damroll());
	        char_target.next_energy_drain = Gamestate.w.clock + 0.4;
    	} else {
	        Gamestate.add_sound("cancel");
            Gamestate.add_message("Resists!",char_target.image,Color.WHITE);
    	}
        m.remove();
    }

    String get_description(){
        return "This evil spell will drain your character's blue bar until they have no energy left.";
    }
}

class Dispel_magic_MA extends Monster_Spell_MA {
    public Dispel_magic_MA(){
        super();
    }

    String name(){
        return "Dispel Magic";
    }

    void make_stuff_happen(){
        Gamestate.add_sound("bad_spell");
        for (Character c : Gamestate.p.chars){
        	if (c.alive && !c.about_to_die){
	        	dispel(c);
        	}
        }
    }

    String get_description(){
        return "A powerful counterspell that removes all your buffs.";
    }
}

class Debuff_disappear_MA extends Monster_action {
    public Debuff_disappear_MA(){
        super();
    }

    String name(){
        return "Drain Magic";
    }

    Double period(){
        return 1.0;
    }

    Double gc(){
        return 2.0;
    }

    boolean back_row_ok(){
        return false;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("wind");
        for (Character c : Gamestate.p.chars){
        	if (c.alive && !c.about_to_die){
	        	if (c.class_code==Class_code.C_PRIEST || c.class_code==Class_code.C_MAGE){
	            	if (!c.draining_resistance){
	            		c.energy = (int)Math.max(0, c.energy - Gamestate.w.randint(4, 8));
	            	}
	        	}
	        	dispel(c);
        	}
        }
        m.remove();
    }

    String get_description(){
        return "Drains the magical energies from your characters, dispelling all buffs and reducing casters' blue bars, and disappears.";
    }
}

class Tornado_disappear_MA extends Tornado_MA {
    public Tornado_disappear_MA(){
        super();
    }

    Double period(){
        return 2.0;
    }

    void make_stuff_happen(){
    	super.make_stuff_happen();
    	m.remove();
    }
}

class Small_claw_MA extends Claw_MA {
    public Small_claw_MA(){
        super();
    }

    Double period(){
        return 5.0;
    }

    int min_damage(){
        return 10;
    }

    int max_damage(){
        return 20;
    }
}

class Back_attack_2_MA extends Back_attack_MA {
    public Back_attack_2_MA(){
        super();
    }

    String name(){
        return "Jab";
    }

    Double delay(){
        return 2.0;
    }

    Double period(){
        return 7.0;
    }

    int min_damage(){
        return 20;
    }

    int max_damage(){
        return 40;
    }
}

class Back_attack_MA extends Smite_MA {
    public Back_attack_MA(){
        super();
    }

    String name(){
        return "Polearm Strike";
    }

    String warning_message(){
        return "Prepares to jab with his long weapon";
    }

    boolean back_row_ok(){
        return true;
    }
    String get_description(){
        return "When it prepares to jab, tell one of your front-row characters to use the Block ability or you will get hit.";
    }
}

class Lunge_2_MA extends Lunge_1_MA {
    public Lunge_2_MA(){
        super();
    }

    Double delay(){
        return 2.0;
    }

    Double period(){
        return 7.0;
    }

    int min_damage(){
        return 20;
    }

    int max_damage(){
        return 40;
    }
}

class Lunge_1_MA extends Smite_MA {
    public Lunge_1_MA(){
        super();
    }

    String name(){
        return "Lunge";
    }

    String warning_message(){
        return "Prepares to lunge";
    }

    boolean enemy_target_front_row_ok(){
        return false;
    }

    boolean enemy_target_back_row_ok(){
        return true;
    }

    String get_description(){
        return "When it prepares to lunge, tell one of your front-row characters to use the Block ability or one of your back-row characters will get hit.";
    }
}

class Snipe_sniper_MA extends Snipe_MA {
    public Snipe_sniper_MA(){
        super();
    }

    Double delay(){
        return 1.5;
    }

    Double period(){
        return 2.5;
    }

    Double gc(){
        return 0.0;
    }

    int min_damage(){
        return 10;
    }

    int max_damage(){
        return 20;
    }

    boolean enemy_target_front_row_ok(){
        return true;
    }

    String get_description(){
        return "He shoots like other monsters you have met, but much faster.";
    }
}

class Snipe_MA extends Monster_action {
    // troglodyte archer version;
    public Snipe_MA(){
        super();
    }

    String name(){
        return "Snipe";
    }

    String warning_message(){
        return "Nocks an arrow";
    }

    Double delay(){
        return 3.0;
    }

    Double period(){
        return 6.0;
    }

    Double gc(){
        return delay();
    }

    int min_damage(){
        return 5;
    }

    int max_damage(){
        return 15;
    }

    boolean front_row_ok(){
        return false;
    }

    boolean target_enemy(){
        return true;
    }

    boolean enemy_target_front_row_ok(){
        return false;
    }

    boolean deflectable(){
        return true;
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_DEFLECT){
    	    	Gamestate.add_sound("deflect");
                c.current_stance = null;
                return true;
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_MANY){
                c.current_stance = null;
                if (Gamestate.w.randint(1,100) <= Math.min(70,success_chance(12-m.level))){
                    return true;
                } else {
                    Gamestate.add_message("Distraction failed",c.image,Color.WHITE);
                }
            }
        }
        return false;
    }

    void make_stuff_happen(){
        if (Gamestate.w.randint(1,100)<=success_chance(char_target.current_evasion()+(4-m.level))){
            Gamestate.add_sound(m.missile_miss_sound);
            Gamestate.add_message("Nimbly dodges!",char_target.image,Color.WHITE);
        } else {
            Gamestate.add_sound(m.missile_hit_sound);
            int damage = Gamestate.w.randint(min_damage(),max_damage());
        	char_target.take_damage(Math.max(0,damage-char_target.current_armor()));
        }
    }

    String get_description(){
        return "When it gets ready to snipe you, tell one of your front-row characters to use the Deflect ability or one of your back-row characters will get hit.";
    }
}

class Snipe_Dark_Elf_Boss_MA extends Snipe_MA {
    public Snipe_Dark_Elf_Boss_MA(){
        super();
    }

    Double delay(){
        return 1.5;
    }

    Double period(){
        return 3.0;
    }

    int min_damage(){
        return 10;
    }

    int max_damage(){
        return 30;
    }

    boolean enemy_target_front_row_ok(){
        return true;
    }

    void make_stuff_happen(){
        if (Gamestate.w.randint(1,100)<=success_chance(char_target.current_evasion()+(4-m.level))){
            Gamestate.add_sound(m.missile_miss_sound);
            Gamestate.add_message("Nimbly dodges!",char_target.image,Color.WHITE);
        } else {
            Gamestate.add_sound(m.missile_hit_sound);
            int damage = Gamestate.w.randint(min_damage(),max_damage());
        	char_target.take_damage(Math.max(0,damage-char_target.current_armor()));
        	if (char_target.alive && !char_target.about_to_die && damage>char_target.current_armor()){
	        	char_target.status_effects.put(Status_effect_code.EF_STUN,new Effect_parameters(Gamestate.w.randint(2,4)));
	        	char_target.action_in_progress = null;
	        	char_target.current_stance = null;
        	}
        }
    }

    String get_description(){
        return "When it gets ready to snipe you, tell one of your front-row characters to use the Deflect ability or one of your characters will get hit.";
    }
}

class Snipe_Dark_Elf_MA extends Snipe_MA {
    public Snipe_Dark_Elf_MA(){
        super();
    }

    Double delay(){
        return 2.0;
    }

    Double period(){
        return 4.5;
    }

    int min_damage(){
        return 6;
    }

    int max_damage(){
        return 18;
    }
}

class Snipe_Naga_MA extends Snipe_MA {
    public Snipe_Naga_MA(){
        super();
    }

    Double delay(){
        return 2.0;
    }

    Double period(){
        return 7.0;
    }

    int min_damage(){
        return 6;
    }

    int max_damage(){
        return 18;
    }
}

class Snipe_Poltergeist_MA extends Snipe_MA {
    public Snipe_Poltergeist_MA(){
        super();
    }

    String name(){
        return "Throws something";
    }

    String warning_message(){
        return "Picks up debris to hurl";
    }

    Double delay(){
        return 2.0;
    }

    Double period(){
        return 3.0;
    }

    boolean front_row_ok(){
        return true;
    }

    int min_damage(){
        return 1;
    }

    int max_damage(){
        return 8;
    }

    void make_stuff_happen(){
        if (Gamestate.w.randint(1,100)<=success_chance(char_target.current_evasion()+(4-m.level))){
            Gamestate.add_sound("whoosh");
            Gamestate.add_message("Nimbly dodges!",char_target.image,Color.WHITE);
        } else {
			Gamestate.add_sound("clunk");
            int damage = Gamestate.w.randint(min_damage(),max_damage());
        	char_target.take_damage(Math.max(0,damage-char_target.current_armor()));
        }
    }
}

class Snipe_Orc_Boss_MA extends Snipe_MA {
    public Snipe_Orc_Boss_MA(){
        super();
    }

    String warning_message(){
        return "Loads his crossbow";
    }

    Double delay(){
        return 2.0;
    }

    Double period(){
        return 9.0;
    }

    int min_damage(){
        return 10;
    }

    int max_damage(){
        return 30;
    }
}

class Snipe_Manticore_MA extends Snipe_MA {
    public Snipe_Manticore_MA(){
        super();
    }

    String warning_message(){
        return "Raises its tail";
    }

    Double delay(){
        return 1.5;
    }

    Double period(){
        return 1.0;
    }

    Double cooldown(){
    	return 2.0;
    }

    int min_damage(){
        return 8;
    }

    int max_damage(){
        return 20;
    }

    void make_stuff_happen(){
        if (Gamestate.w.randint(1,100)<=success_chance(char_target.current_evasion()+(4-m.level))){
            Gamestate.add_sound("whoosh");
            Gamestate.add_message("Nimbly dodges!",char_target.image,Color.WHITE);
        } else {
            Gamestate.add_sound("manticore");
            int damage = Gamestate.w.randint(min_damage(),max_damage());
        	char_target.take_damage(Math.max(0,damage-char_target.current_armor()));
        }
    }
}

class Summon_mountain_animals_MA extends Summon_Animals_MA {
    public Summon_mountain_animals_MA(){
        super();
    }

    String name(){
        return "Summon Mountain Animals";
    }

    int n_summoned(){
    	return 3;
    }

    List<Monster_code> choose_from(){
        return Arrays.asList(Monster_code.MOB_WOLF,Monster_code.MOB_BEAR,Monster_code.MOB_RAVEN,Monster_code.MOB_WOLF,Monster_code.MOB_BEAR,Monster_code.MOB_RAVEN,Monster_code.MOB_WOLF,Monster_code.MOB_BEAR,Monster_code.MOB_RAVEN,Monster_code.MOB_WHITE_WOLF,Monster_code.MOB_WHITE_WOLF,Monster_code.MOB_BABY_BLUE_DRAGON,Monster_code.MOB_BABY_BLUE_DRAGON,Monster_code.MOB_ROC);
    }

    String thing_summoned(){
        return "beasts of the mountains";
    }
}

class Summon_doppelganger_MA extends Summon_Snakes_MA {
    public Summon_doppelganger_MA(){
        super();
    }

    String name(){
        return "Summon Doppelganger";
    }

    Double period(){
        return 14.0;
    }

    Double cooldown(){
        return 99.0;
    }

    int n_summoned(){
        return 1;
    }

    List<Monster_code> choose_from(){
        return Arrays.asList(Monster_code.MOB_DOPPELGANGER);
    }

    String thing_summoned(){
        return "doppelganger";
    }

    String get_description(){
        return "Summons up to " + (n_summoned()) + " " + thing_summoned() + ". Cannot be prevented.";
    }
}

class Summon_minor_demons_MA extends Summon_Snakes_MA {
    public Summon_minor_demons_MA(){
        super();
    }

    String name(){
        return "Summon Minor Demon";
    }

    Integer n_uses(){
        return 2;
    }

    int n_summoned(){
        return 1;
    }

    List<Monster_code> choose_from(){
        return Arrays.asList(Monster_code.MOB_QUASIT,Monster_code.MOB_IMP);
    }

    String thing_summoned(){
        return "minor demons";
    }
}

class Summon_Snakes_MA extends Monster_action {
    public Summon_Snakes_MA(){
        super();
    }

    String name(){
        return "Summon Snakes";
    }

    boolean ok(){
        if (6-Gamestate.e.n_mobs_taking_up_space()>=n_summoned()){
            return super.ok();
        } else {
            return false;
        }
    }

    Double period(){
        return 10.0;
    }

    Double likelihood_increase_until_n_uses(){
        return 2.0;
    }

    Integer n_uses(){
        return 4;
    }

    Double cooldown(){
        return 3.0;
    }

    int n_summoned(){
        return 3;
    }

    List<Monster_code> choose_from(){
        return Arrays.asList(Monster_code.MOB_SNAKE);
    }

    void make_stuff_happen(){
        if (m.front_row() && Gamestate.e.last_free_back_pos()!=null){
            m.slide_from = m.pos;
            m.pos = Gamestate.e.last_free_back_pos();
            m.slide_to = m.pos;
            m.slide_start = Gamestate.w.clock;
            m.slide_finish = Gamestate.w.clock + 0.1;
        }
        int summoned = 0;
        int to_summon = n_summoned();
        for (int i=0; i<to_summon; i++){
            Mob new_critter = Gamestate.e.add_mob(choose_from().get(Gamestate.w.randint(0,choose_from().size()-1)));
            if (new_critter!=null){
                new_critter.slide_from = null;
                new_critter.fade_in = true;
                new_critter.fade_start = Gamestate.w.clock;
                new_critter.fade_finish = Gamestate.w.clock + 0.75;
                new_critter.summoner = m;
                summoned = summoned + 1;
            }
        }
        if (summoned>0)
            Gamestate.add_sound("summon");
        if (!m.front_row() && Gamestate.e.n_surviving_front_mobs()==0){
            m.slide_from = m.pos;
            m.pos = Gamestate.e.last_free_front_pos();
            m.slide_to = m.pos;
            m.slide_start = Gamestate.w.clock;
            m.slide_finish = Gamestate.w.clock + 0.1;
        }
    }

    String thing_summoned(){
        return "poisonous little critters";
    }

    String get_description(){
        return "Summons up to " + (n_summoned()) + " " + thing_summoned() + ". Cannot be prevented.";
    }
}

class Summon_white_wolves_MA extends Summon_Snakes_MA {
    public Summon_white_wolves_MA(){
        super();
    }

    String name(){
        return "Summon White Wolves";
    }

    Double period(){
        return 100.0;
    }

    Double likelihood_increase_until_n_uses(){
        return 50.0;
    }

    Integer n_uses(){
        return 2;
    }

    Double cooldown(){
        return 12.0;
    }

    int n_summoned(){
        return 3;
    }

    List<Monster_code> choose_from(){
        return Arrays.asList(Monster_code.MOB_WHITE_WOLF);
    }

    String thing_summoned(){
        return "fierce white wolves";
    }
}

class Summon_Animals_MA extends Summon_Snakes_MA {
    public Summon_Animals_MA(){
        super();
    }

    String name(){
        return "Summon Animals";
    }

    Double period(){
        return 13.0;
    }

    int n_summoned(){
        return 3;
    }

    List<Monster_code> choose_from(){
        return Arrays.asList(Monster_code.MOB_CENTIPEDE,Monster_code.MOB_SNAKE,Monster_code.MOB_COBRA,Monster_code.MOB_GIANT_FIREFLY,Monster_code.MOB_GIANT_WASP,Monster_code.MOB_MILLIPEDE,Monster_code.MOB_PYTHON,Monster_code.MOB_WOLF,Monster_code.MOB_WOLF,Monster_code.MOB_BEAR,Monster_code.MOB_BEAR,Monster_code.MOB_SCORPION,Monster_code.MOB_BEETLE,Monster_code.MOB_GIANT_ANT,Monster_code.MOB_BIG_SPIDER,Monster_code.MOB_RAVEN,Monster_code.MOB_BAD_CAT,Monster_code.MOB_BIG_FROG);
    }

    String thing_summoned(){
        return "animals";
    }
}

class Summon_Cobras_MA extends Summon_Snakes_MA {
    public Summon_Cobras_MA(){
        super();
    }

    String name(){
        return "Summon Cobras";
    }

    Double cooldown(){
        return 7.0;
    }

    int n_summoned(){
        return 3;
    }

    List<Monster_code> choose_from(){
        return Arrays.asList(Monster_code.MOB_COBRA);
    }

    String thing_summoned(){
        return "spitting cobras";
    }
}

class Summon_Evil_Trash_MA extends Summon_Snakes_MA {
    public Summon_Evil_Trash_MA(){
        super();
    }

    String name(){
        return "Summon Minions";
    }

    int n_summoned(){
        return 3;
    }

    List<Monster_code> choose_from(){
        return Arrays.asList(Monster_code.MOB_KOBOLD,Monster_code.MOB_ORC_WRETCH,Monster_code.MOB_HARPY,Monster_code.MOB_ORC_ROCK_THROWER,Monster_code.MOB_TROGLODYTE,Monster_code.MOB_ORC_WARRIOR,Monster_code.MOB_SKELETON,Monster_code.MOB_TROGLODYTE_ARCHER,Monster_code.MOB_ORC_SLAVEMASTER,Monster_code.MOB_ORC_WITH_MANCATCHER,Monster_code.MOB_SPECTRE,Monster_code.MOB_TROGLODYTE_SHAMAN,Monster_code.MOB_THUG,Monster_code.MOB_BIG_KOBOLD,Monster_code.MOB_GOBLIN,Monster_code.MOB_YOUNG_NECROMANCER,Monster_code.MOB_HALF_TROLL);
    }

    String thing_summoned(){
        return "weak evil monsters";
    }
}

class Summon_Golem_MA extends Summon_Snakes_MA {
    public Summon_Golem_MA(){
        super();
    }

    String name(){
        return "Summon Golem";
    }

    int n_summoned(){
        return 1;
    }

    boolean ok(){
        if (super.ok()){
            int count = 0;
            for (Mob mm : Gamestate.e.mobs)
                if (mm.alive && !mm.about_to_die && mm.construct)
                    count = count + 1;
            if (count==0)
                return true;
        }
        return false;
    }

    Double period(){
        return 30.0;
    }

    Double likelihood_increase_until_n_uses(){
        return 10.0;
    }

    Integer n_uses(){
        return 1;
    }

    List<Monster_code> choose_from(){
        return Arrays.asList(Monster_code.MOB_STONE_GOLEM);
    }

    String thing_summoned(){
        return "golem";
    }
}

class Summon_wolves_MA extends Summon_Snakes_MA {
    public Summon_wolves_MA(){
        super();
    }

    String name(){
        return "Summon Wolves";
    }

    Double period(){
        return 25.0;
    }

    Double likelihood_increase_until_n_uses(){
        return 5.0;
    }

    Integer n_uses(){
        return 3;
    }

    int n_summoned(){
        return 3;
    }

    List<Monster_code> choose_from(){
        return Arrays.asList(Monster_code.MOB_WOLF,Monster_code.MOB_WHITE_WOLF);
    }

    String thing_summoned(){
        return "wolves";
    }
}

class Summon_Orcs_MA extends Summon_Snakes_MA {
    public Summon_Orcs_MA(){
        super();
    }

    String name(){
        return "Summon Orcs";
    }

    Double period(){
        return 25.0;
    }

    Double likelihood_increase_until_n_uses(){
        return 5.0;
    }

    Integer n_uses(){
        return 4;
    }

    int n_summoned(){
        return 5;
    }

    List<Monster_code> choose_from(){
        return Arrays.asList(Monster_code.MOB_ORC_WRETCH,Monster_code.MOB_ORC_ROCK_THROWER,Monster_code.MOB_ORC_WARRIOR,Monster_code.MOB_ORC_ROCK_THROWER,Monster_code.MOB_ORC_WARRIOR,Monster_code.MOB_ORC_ROCK_THROWER,Monster_code.MOB_ORC_WARRIOR,Monster_code.MOB_ORC_AGITATOR,Monster_code.MOB_ORC_SLAVEMASTER,Monster_code.MOB_ORC_CHAMPION);
    }

    String thing_summoned(){
        return "assorted orcs";
    }
}

class Summon_Reptiles_MA extends Summon_Snakes_MA {
    public Summon_Reptiles_MA(){
        super();
    }

    String name(){
        return "Summon Reptiles";
    }

    int n_summoned(){
        return 3;
    }

    List<Monster_code> choose_from(){
        return Arrays.asList(Monster_code.MOB_SNAKE,Monster_code.MOB_COBRA,Monster_code.MOB_PYTHON,Monster_code.MOB_SNAKE,Monster_code.MOB_COBRA,Monster_code.MOB_PYTHON,Monster_code.MOB_TURTLOID,Monster_code.MOB_NAGA_ARCHER,Monster_code.MOB_NAGA_WARRIOR,Monster_code.MOB_BASILISK);
    }

    String thing_summoned(){
        return "assorted reptiles";
    }
}

class Summon_Troglodytes_MA extends Summon_Snakes_MA {
    public Summon_Troglodytes_MA(){
        super();
    }

    String name(){
        return "Summon Troglodytes";
    }

    int n_summoned(){
        return 3;
    }

    List<Monster_code> choose_from(){
        return Arrays.asList(Monster_code.MOB_TROGLODYTE,Monster_code.MOB_TROGLODYTE_ARCHER,Monster_code.MOB_TROGLODYTE_ARCHER,Monster_code.MOB_TROGLODYTE_SHAMAN,Monster_code.MOB_TROGLODYTE_PRIEST);
    }

    String thing_summoned(){
        return "assorted troglodytes";
    }
}

class Summon_illusion_MA extends Summon_illusion_often_MA {
    public Summon_illusion_MA(){
        super();
    }

    Double period(){
        return 6.0;
    }
}

class Summon_illusion_often_MA extends Summon_Various_MA {
    public Summon_illusion_often_MA(){
        super();
    }

    Double period(){
        return 2.0;
    }

    Double cooldown(){
        return 4.0;
    }

    String name(){
        return "Summon Illusion";
    }

    int n_summoned(){
        return 1;
    }

    void make_stuff_happen(){
        if (m.front_row() && Gamestate.e.last_free_back_pos()!=null){
            m.slide_from = m.pos;
            m.pos = Gamestate.e.last_free_back_pos();
            m.slide_to = m.pos;
            m.slide_start = Gamestate.w.clock;
            m.slide_finish = Gamestate.w.clock + 0.1;
        }
        int summoned = 0;
        int to_summon = n_summoned();
        for (int i=0; i<to_summon; i++){
            Mob new_critter = Gamestate.e.add_mob(choose_from().get(Gamestate.w.randint(0,choose_from().size()-1)));
            if (new_critter!=null){
                new_critter.slide_from = null;
                new_critter.fade_in = true;
                new_critter.fade_start = Gamestate.w.clock;
                new_critter.fade_finish = Gamestate.w.clock + 0.75;
                new_critter.summoner = m;
                new_critter.illusion = true; // the only difference
                summoned = summoned + 1;
            }
        }
        if (summoned>0)
            Gamestate.add_sound("summon");
        if (!m.front_row() && Gamestate.e.n_surviving_front_mobs()==0){
            m.slide_from = m.pos;
            m.pos = Gamestate.e.last_free_front_pos();
            m.slide_to = m.pos;
            m.slide_start = Gamestate.w.clock;
            m.slide_finish = Gamestate.w.clock + 0.1;
        }
    }

    String thing_summoned(){
        return "an illusionary monster";
    }
}

class Summon_Various_MA extends Summon_Snakes_MA {
    public Summon_Various_MA(){
        super();
    }

    Double period(){
        return 6.0;
    }

    Double likelihood_increase_until_n_uses(){
        return 1.0;
    }

    Integer n_uses(){
        return 999;
    }

    Double cooldown(){
        return 2.0;
    }

    String name(){
        return "Summon Monsters";
    }

    int n_summoned(){
        return 3;
    }

    List<Monster_code> choose_from(){
        return Arrays.asList(Monster_code.MOB_YOUNG_NECROMANCER,Monster_code.MOB_BIG_SPIDER,Monster_code.MOB_HELLHOUND,Monster_code.MOB_DARK_ELF_DRUID,Monster_code.MOB_MUMMY,Monster_code.MOB_POLTERGEIST,Monster_code.MOB_BERSERKER,Monster_code.MOB_BLAST_SPORE,Monster_code.MOB_COBRA,Monster_code.MOB_GIANT_FIREFLY,Monster_code.MOB_GIANT_WASP,Monster_code.MOB_JELLY,Monster_code.MOB_MILLIPEDE,Monster_code.MOB_ORC_WARRIOR,Monster_code.MOB_PYTHON,Monster_code.MOB_SKELETON,Monster_code.MOB_TROGLODYTE_ARCHER,Monster_code.MOB_WOLF,Monster_code.MOB_LEPRECHAUN,Monster_code.MOB_OOZE,Monster_code.MOB_ORC_SLAVEMASTER,Monster_code.MOB_ORC_WITH_MANCATCHER,Monster_code.MOB_SPECTRE,Monster_code.MOB_TROGLODYTE_SHAMAN,Monster_code.MOB_CLAY_GOLEM,Monster_code.MOB_NAGA_ARCHER,Monster_code.MOB_NAGA_WARRIOR,Monster_code.MOB_OGRE,Monster_code.MOB_ORC_CHAMPION,Monster_code.MOB_TROGLODYTE_PRIEST,Monster_code.MOB_TURTLOID,Monster_code.MOB_WHITE_WOLF,Monster_code.MOB_WIGHT,Monster_code.MOB_WRAITH,Monster_code.MOB_ARTIFICER,Monster_code.MOB_BASILISK,Monster_code.MOB_BLACK_KNIGHT,Monster_code.MOB_BLACK_OGRE,Monster_code.MOB_BURNING_GHOST,Monster_code.MOB_DARK_DWARF,Monster_code.MOB_DARK_ELF_ARCHER,Monster_code.MOB_DARK_PRIEST,Monster_code.MOB_LIME_JELLY,Monster_code.MOB_MAGE,Monster_code.MOB_MAGEBANE_EYE,Monster_code.MOB_MUTE_GHOST,Monster_code.MOB_SHADOW,Monster_code.MOB_MANTICORE,Monster_code.MOB_TROLL,Monster_code.MOB_FROZEN_SPIRIT,Monster_code.MOB_THUG,Monster_code.MOB_BIG_KOBOLD,Monster_code.MOB_GOBLIN,Monster_code.MOB_BEETLE,Monster_code.MOB_MINOTAUR,Monster_code.MOB_SCORPION,Monster_code.MOB_BEAR,Monster_code.MOB_RAVEN,Monster_code.MOB_HALBERDIER,Monster_code.MOB_HALF_TROLL,Monster_code.MOB_BABY_WHITE, Monster_code.MOB_BABY_RED_DRAGON,Monster_code.MOB_BABY_GREEN_DRAGON,Monster_code.MOB_BABY_BLUE_DRAGON,Monster_code.MOB_WHIRLWIND,Monster_code.MOB_IMP,Monster_code.MOB_QUASIT,Monster_code.MOB_GUARDIAN_EYE,Monster_code.MOB_ILLUSIONIST,Monster_code.MOB_TIME_VORTEX,Monster_code.MOB_LIGHTNING_DOG,Monster_code.MOB_BIG_SNAKE,Monster_code.MOB_VORTEX,Monster_code.MOB_CLAWED_SPECTRE,Monster_code.MOB_WINGED_SPECTRE,Monster_code.MOB_EYE_OF_NEGATION,Monster_code.MOB_DRIDER,Monster_code.MOB_LIZARDMAN);
    }

    String thing_summoned(){
        return "assorted monsters";
    }
}

class Summon_Front_MA extends Summon_Snakes_MA {
    public Summon_Front_MA(){
        super();
    }

    boolean ok(){
        return m.front_row() && super.ok();
    }

    Double period(){
        return 2.0;
    }

    Double cooldown(){
        return 10.0;
    }

    String name(){
        return "Summon Protector";
    }

    int n_summoned(){
        return 1;
    }

    List<Monster_code> choose_from(){
        return Arrays.asList(Monster_code.MOB_OOZE,Monster_code.MOB_CLAY_GOLEM,Monster_code.MOB_NAGA_WARRIOR,Monster_code.MOB_OGRE,Monster_code.MOB_ORC_CHAMPION,Monster_code.MOB_WHITE_WOLF,Monster_code.MOB_WIGHT,Monster_code.MOB_BASILISK,Monster_code.MOB_BLACK_KNIGHT,Monster_code.MOB_BLACK_OGRE,Monster_code.MOB_DARK_DWARF,Monster_code.MOB_LIME_JELLY,Monster_code.MOB_TROLL,Monster_code.MOB_MINOTAUR,Monster_code.MOB_HELLHOUND,Monster_code.MOB_HALF_TROLL,Monster_code.MOB_LIGHTNING_DOG,Monster_code.MOB_BIG_SNAKE,Monster_code.MOB_DRIDER,Monster_code.MOB_LIZARDMAN);
    }

    String thing_summoned(){
        return "a strong front row monster";
    }
}

class Summon_Bodyguard_MA extends Summon_Snakes_MA {
    public Summon_Bodyguard_MA(){
        super();
    }

    Double period(){
        return 30.0;
    }

    Double likelihood_increase_until_n_uses(){
        return 30.0;
    }

    Integer n_uses(){
        return 1;
    }

    String name(){
        return "Summon Bodyguard";
    }

    int n_summoned(){
        return 1;
    }

    List<Monster_code> choose_from(){
        return Arrays.asList(Monster_code.MOB_COBRA,Monster_code.MOB_JELLY,Monster_code.MOB_MILLIPEDE,Monster_code.MOB_ORC_WARRIOR,Monster_code.MOB_PYTHON,Monster_code.MOB_SKELETON,Monster_code.MOB_WOLF,Monster_code.MOB_OOZE,Monster_code.MOB_ORC_WITH_MANCATCHER,Monster_code.MOB_CLAY_GOLEM,Monster_code.MOB_NAGA_WARRIOR,Monster_code.MOB_OGRE,Monster_code.MOB_ORC_CHAMPION,Monster_code.MOB_TURTLOID,Monster_code.MOB_WHITE_WOLF,Monster_code.MOB_WIGHT,Monster_code.MOB_BLACK_KNIGHT,Monster_code.MOB_BLACK_OGRE,Monster_code.MOB_DARK_DWARF,Monster_code.MOB_TROLL,Monster_code.MOB_THUG,Monster_code.MOB_BEETLE,Monster_code.MOB_MINOTAUR,Monster_code.MOB_SCORPION,Monster_code.MOB_BEAR,Monster_code.MOB_BIG_SPIDER,Monster_code.MOB_HELLHOUND,Monster_code.MOB_HALF_TROLL,Monster_code.MOB_DRIDER,Monster_code.MOB_LIZARDMAN);
    }

    String thing_summoned(){
        return "a front row monster";
    }
}

class Summon_Undead_MA extends Summon_Snakes_MA {
    public Summon_Undead_MA(){
        super();
    }

    String name(){
        return "Summon Undead";
    }

    Double period(){
        return 8.0;
    }

    int n_summoned(){
        return 1;
    }

    List<Monster_code> choose_from(){
        return Arrays.asList(Monster_code.MOB_SKELETON,Monster_code.MOB_SKELETON,Monster_code.MOB_SKELETON,Monster_code.MOB_SPECTRE,Monster_code.MOB_SPECTRE,Monster_code.MOB_WIGHT,Monster_code.MOB_WIGHT,Monster_code.MOB_WRAITH,Monster_code.MOB_WRAITH,Monster_code.MOB_BURNING_GHOST,Monster_code.MOB_MUTE_GHOST,Monster_code.MOB_SHADOW,Monster_code.MOB_SHADOW,Monster_code.MOB_FROZEN_SPIRIT,Monster_code.MOB_FROZEN_SPIRIT,Monster_code.MOB_MUMMY,Monster_code.MOB_POLTERGEIST,Monster_code.MOB_CLAWED_SPECTRE,Monster_code.MOB_WINGED_SPECTRE,Monster_code.MOB_GIANT_SKELETON);
    }

    String thing_summoned(){
        return "random undead";
    }
}

class Summon_Low_Undead_MA extends Summon_Snakes_MA {
    public Summon_Low_Undead_MA(){
        super();
    }

    String name(){
        return "Summon Undead";
    }

    int n_summoned(){
        return 1;
    }

    List<Monster_code> choose_from(){
        return Arrays.asList(Monster_code.MOB_SKELETON,Monster_code.MOB_SKELETON,Monster_code.MOB_SKELETON,Monster_code.MOB_SPECTRE,Monster_code.MOB_FROZEN_SPIRIT,Monster_code.MOB_POLTERGEIST);
    }

    String thing_summoned(){
        return "random low-grade undead";
    }
}

class Summon_Wretch_MA extends Summon_Snakes_MA {
    public Summon_Wretch_MA(){
        super();
    }

    String name(){
        return "Summon Wretch";
    }

    Double period(){
        return 4.0;
    }

    Double likelihood_increase_until_n_uses(){
        return 1.0;
    }

    Integer n_uses(){
        return 999;
    }

    int n_summoned(){
        return 1;
    }

    List<Monster_code> choose_from(){
        return Arrays.asList(Monster_code.MOB_ORC_WRETCH);
    }

    String thing_summoned(){
        return "pitiful orcish thrall";
    }
}

class Gaze_petrify_MA extends Monster_action {
    public Gaze_petrify_MA(){
        super();
    }

    String name(){
        return "Petrifying Gaze";
    }

    String warning_message(){
        return "Gazes at you";
    }

    boolean target_enemy(){
        return true;
    }

    boolean enemy_target_ok(Character c){
        return super.enemy_target_ok(c) && !c.has_status_effect(Status_effect_code.EF_PETRIFY);
    }

    Double period(){
        return 10.0;
    }

    Double delay(){
        return 1.2;
    }

    Double cooldown(){
        return 20.0;
    }

    Double gc(){
        return 2.0;
    }

    void make_stuff_happen(){
        boolean immune = false;
        for (Character c : Gamestate.p.chars){
        	if (c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_BOLTS)
    	    	Gamestate.add_sound("fail");
        		c.current_stance = null;
        		immune = true;
        }
        if (char_target.alive && !char_target.about_to_die){
            for (Character_action a : char_target.actions){
                if (a.action_code==Character_action_code.CA_STEELY_GAZE){
                    Gamestate.add_message("Immune to gaze attacks!",char_target.image,Color.WHITE);
                    immune = true;
                    continue;
                }
            }
            if (!immune){
    	        Gamestate.add_sound("grunt");
            	char_target.status_effects.put(Status_effect_code.EF_PETRIFY,new Effect_parameters(9999.9));
            	char_target.action_in_progress = null;
            	char_target.current_stance = null;
            	char_target.health = 1;
            }
        }
    }

    String get_description(){
        return "Turns one of your party members to stone! A mirrored helmet would protect them, or the Reflection spell (but you must use the spell quickly, and your characters will not cast it unless they are told to).";
    }
}

class Gaze_Scare_All_MA extends Gaze_Paralysis_MA {
    public Gaze_Scare_All_MA(){
        super();
    }

    String name(){
        return "Terrifying Gaze";
    }

    Double period(){
        return 5.0;
    }

    Double delay(){
        return 2.0;
    }

    boolean paralysing(){
        return false;
    }

    boolean scary(){
        return true;
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_FEAR){
    	    	Gamestate.add_sound("fail");
                c.current_stance = null;
                m.countered_at = Gamestate.w.clock;
                return true;
            }
        }
        return false;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("bad_spell");
    	if (Gamestate.p.fear_resistant()) return;
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die){
                boolean immune = false;
                for (Character_action a : c.actions){
                    if (a.action_code==Character_action_code.CA_STEELY_GAZE || a.action_code==Character_action_code.CA_RESIST_FEAR){
                        immune = true;
                        continue;
                    }
                }
                if (c.has_status_effect(Status_effect_code.EF_MEDITATION))
                    immune = true;
                if (!immune){
                    c.status_effects.put(Status_effect_code.EF_FEAR,new Effect_parameters(duration()));
                    c.action_in_progress = null;
                    c.current_stance = null;
                }
            }
        }
    }

    String get_description(){
        return "Scares your entire party. Can be countered with the 'Counter: Fear' spell.";
    }
}

class Gaze_Paralysis_MA extends Monster_action {
    public Gaze_Paralysis_MA(){
        super();
    }

    String name(){
        return "Paralysing Gaze";
    }

    String warning_message(){
        return "Gazes at you";
    }

    Double period(){
        return 15.0;
    }

    Double delay(){
        return 3.0;
    }

    Double cooldown(){
        return 10.0;
    }

    Double gc(){
        return 3.0;
    }

    boolean paralysing(){
        return true;
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_PARALYSIS){
    	    	Gamestate.add_sound("fail");
                c.current_stance = null;
                m.countered_at = Gamestate.w.clock;
                return true;
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_MANY){
                c.current_stance = null;
                if (Gamestate.w.randint(1,100) <= Math.min(70,success_chance(12-m.level))){
                    return true;
                } else {
                    Gamestate.add_message("Distraction failed",c.image,Color.WHITE);
                }
            }
        }
        return false;
    }

    double duration(){
        return Gamestate.w.randint(5,10);
    }

    void make_stuff_happen(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die){
                boolean immune = false;
                for (Character_action a : c.actions){
                    if (a.action_code==Character_action_code.CA_STEELY_GAZE || a.action_code==Character_action_code.CA_FREE_ACTION){
                        immune = true;
                        continue;
                    }
                }
                if (c.has_status_effect(Status_effect_code.EF_MEDITATION))
                    immune = true;
                if (!immune){
        	        Gamestate.add_sound("grunt");
                    c.status_effects.put(Status_effect_code.EF_PARALYSIS,new Effect_parameters(duration()));
                    c.action_in_progress = null;
                    c.current_stance = null;
                }
            }
        }
    }

    String get_description(){
        return "Paralyses your entire party. Can be countered with the 'Counter: Paralysis' spell.";
    }
}

class Sting_Paralysis_MA extends Monster_action {
    public Sting_Paralysis_MA(){
        super();
    }

    String name(){
        return "Paralysing Sting";
    }
    
    String message(){
        return "";
    }    

    Double period(){
        return 10.0;
    }

    Double cooldown(){
        return 10.0;
    }

    boolean paralysing(){
        return true;
    }

    double duration(){
        return Gamestate.w.randint(5,10);
    }

    boolean back_row_ok(){
        return false;
    }

    boolean target_enemy(){
        return true;
    }

    boolean enemy_target_back_row_ok(){
        return false;
    }

    void make_stuff_happen(){
        if (m.melee_hit_roll(char_target)){
            Gamestate.add_sound(m.melee_hit_sound);
            int damage = m.melee_damage_roll();
            int armor = char_target.current_armor();
            if (damage > armor){
            	char_target.take_damage(Math.max(0,damage-armor));
                m.flash_image = "pale";
                if (char_target.alive && !char_target.about_to_die){
                    boolean immune = false;
                    for (Character_action a : char_target.actions){
                        if (a.action_code==Character_action_code.CA_FREE_ACTION){
                            immune = true;
                            continue;
                        }
                    }
                    if (char_target.has_status_effect(Status_effect_code.EF_MEDITATION))
                        immune = true;
                    if (immune){
                        Gamestate.add_message("Failed to paralyse " + char_target.name,m.image,Color.WHITE);
                    } else {
            	        Gamestate.add_sound("grunt");
                    	char_target.status_effects.put(Status_effect_code.EF_PARALYSIS,new Effect_parameters(duration()));
                    	char_target.action_in_progress = null;
                    	char_target.current_stance = null;
                        Gamestate.add_message("Paralysing Sting!  [" + char_target.name + "]",m.image,Color.WHITE);
                    }
                }
            } else {
                Gamestate.add_message("Failed to paralyse " + char_target.name,m.image,Color.WHITE);
            }
        } else {
            Gamestate.add_sound(m.melee_miss_sound);
            Gamestate.add_message("Failed to paralyse " + char_target.name,m.image,Color.WHITE);
        }
    }

    String get_description(){
        return "Its sting paralyses a character for a few seconds - if it can penetrate their armour.";
    }
}

class Squark_MA extends Gaze_Scare_MA {
    public Squark_MA(){
        super();
    }

    String message(){
        return "Cawwww!!!";
    }

    String name(){
        return "Caw";
    }

    String warning_message(){
        return "";
    }

    Double period(){
        return 7.0;
    }

    Double delay(){
        return 0.0;
    }

    double duration(){
        return Gamestate.w.randint(2,5);
    }

    String get_description(){
        return "Makes one of your characters afraid.";
    }
}

class Gaze_Scare_MA extends Monster_action {
    public Gaze_Scare_MA(){
        super();
    }

    String name(){
        return "Terrifying Gaze";
    }

    String warning_message(){
        return "Gazes at you";
    }

    Double likelihood_increase_until_n_uses(){
        return 2.0;
    }

    Integer n_uses(){
        return 1;
    }

    Double period(){
        return 4.0;
    }

    Double delay(){
        return 2.5;
    }

    Double cooldown(){
        return 5.0;
    }

    Double gc(){
        return 1.5;
    }

    boolean target_enemy(){
        return true;
    }

    double duration(){
        return Gamestate.w.randint(8,12);
    }

    boolean scary(){
        return true;
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_FEAR){
    	    	Gamestate.add_sound("fail");
                c.current_stance = null;
                m.countered_at = Gamestate.w.clock;
                return true;
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_MANY){
                c.current_stance = null;
                if (Gamestate.w.randint(1,100) <= Math.min(70,success_chance(12-m.level))){
                    return true;
                } else {
                    Gamestate.add_message("Distraction failed",c.image,Color.WHITE);
                }
            }
        }
        return false;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("bad_spell");
    	if (Gamestate.p.fear_resistant()) return;
        if (char_target.alive && !char_target.about_to_die){
            boolean immune = false;
            for (Character_action a : char_target.actions){
                if (a.action_code==Character_action_code.CA_STEELY_GAZE || a.action_code==Character_action_code.CA_RESIST_FEAR){
                    immune = true;
                    continue;
                }
            }
            if (char_target.has_status_effect(Status_effect_code.EF_MEDITATION))
                immune = true;
            if (!immune){
            	char_target.status_effects.put(Status_effect_code.EF_FEAR,new Effect_parameters(duration()));
            	char_target.action_in_progress = null;
            	char_target.current_stance = null;
            }
        }
    }

    String get_description(){
        return "Makes one of your characters afraid. Can be countered with the 'Counter: Fear' spell.";
    }
}

class Curse_MA extends Monster_action {
    public Curse_MA(){
        super();
    }

    String name(){
        return "Curse";
    }

    Double likelihood_increase_until_n_uses(){
        return 5.0;
    }

    Integer n_uses(){
        return 1;
    }

    Double period(){
        return 6.0;
    }

    Double cooldown(){
        return 5.0;
    }

    double duration(){
        return Gamestate.w.randint(20,40);
    }

    void make_stuff_happen(){
        Gamestate.add_sound("bad_spell");
        ArrayList<Character> options = new ArrayList<Character>();
        for (Character c : Gamestate.p.chars)
            if (c.alive && !c.about_to_die)
                options.add(c);
        if (options.size()==0) return;
        Character target = options.get(Gamestate.w.randint(0,options.size()-1));
        target.status_effects.put(Status_effect_code.EF_CURSE,new Effect_parameters(duration()));
    }

    String get_description(){
        return "Curses one of your characters. Cannot be prevented. All monsters will try to attack this character and they will take extra damage from all sources.";
    }
}

class Howl_MA extends Monster_action {
    public Howl_MA(){
        super();
    }

    String name(){
        return "Howl";
    }
    
    String message(){
        return "Hoooowlll!!";
    }

    Double likelihood_increase_until_n_uses(){
        return 2.0;
    }

    Integer n_uses(){
        return 1;
    }

    Double period(){
        return 15.0;
    }

    Double cooldown(){
        return 5.0;
    }

    Double gc(){
        return 3.0;
    }

    double duration(){
        return Gamestate.w.randint(4,8);
    }

    boolean scary(){
        return true;
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_FEAR){
    	    	Gamestate.add_sound("fail");
                c.current_stance = null;
                m.countered_at = Gamestate.w.clock;
                return true;
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_MANY){
                c.current_stance = null;
                if (Gamestate.w.randint(1,100) <= Math.min(70,success_chance(12-m.level))){
                    return true;
                } else {
                    Gamestate.add_message("Distraction failed",c.image,Color.WHITE);
                }
            }
        }
        return false;
    }

    void make_stuff_happen(){
        Gamestate.add_sound(sound());
    	if (Gamestate.p.fear_resistant()) return;
        while (true){
        	Character char_target = Gamestate.p.random_target(null);
            if (char_target.alive && !char_target.about_to_die){
                boolean immune = false;
                for (Character_action a : char_target.actions){
                    if (a.action_code==Character_action_code.CA_RESIST_FEAR){
                        immune = true;
                        continue;
                    }
                }
                if (char_target.has_status_effect(Status_effect_code.EF_MEDITATION))
                    immune = true;
                if (!immune){
                	char_target.status_effects.put(Status_effect_code.EF_FEAR,new Effect_parameters(duration()));
                	char_target.action_in_progress = null;
                	char_target.current_stance = null;
                }
            }
            if (!Gamestate.w.one_in(3))
                return;
        }
    }

    String sound(){
    	return "howl";}

    String get_description(){
        return "Makes one or more of your characters afraid. Can be countered with the 'Counter: Fear' spell.";
    }
}

class Bird_shriek_Scare_MA extends Shriek_Scare_MA {
    public Bird_shriek_Scare_MA(){
        super();
    }

    String message(){
        return "SKRAAAAK!";
    }

    String name(){
        return "Caw";
    }
    
    String sound(){
    	return "scream";}
}

class Shriek_Scare_MA extends Howl_MA {
    public Shriek_Scare_MA(){
        super();
    }

    String name(){
        return "Wail";
    }
    
    String message(){
        return "EEEEeeeeEEEEEE!!!";
    }

    Double period(){
        return 8.0;
    }

    Double cooldown(){
        return 3.0;
    }

    Double gc(){
        return 1.0;
    }

    String sound(){
    	return "scream";}
}

class Gaze_Sleep_MA extends Monster_action {
    public Gaze_Sleep_MA(){
        super();
    }

    String name(){
        return "Hypnotic Gaze";
    }

    String warning_message(){
        return "Gazes at you";
    }

    boolean sleepy(){
        return true;
    }

    Double period(){
        return 10.0;
    }

    Double delay(){
        return 2.5;
    }

    Double gc(){
        return 3.0;
    }

    Double cooldown(){
        return 5.0;
    }

    double duration(){
        return Gamestate.w.randint(8,12);
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_SLEEP){
    	    	Gamestate.add_sound("fail");
                c.current_stance = null;
                m.countered_at = Gamestate.w.clock;
                return true;
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_MANY){
                c.current_stance = null;
                if (Gamestate.w.randint(1,100) <= Math.min(70,success_chance(12-m.level))){
                    return true;
                } else {
                    Gamestate.add_message("Distraction failed",c.image,Color.WHITE);
                }
            }
        }
        return false;
    }

    void make_stuff_happen(){
    	boolean any_sleep = false;
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die){
                boolean immune = false;
                for (Character_action a : c.actions){
                    if (a.action_code==Character_action_code.CA_STEELY_GAZE){
                        immune = true;
                        continue;
                    }
                }
                if (c.has_status_effect(Status_effect_code.EF_MEDITATION))
                    immune = true;
                if (c.sleep_resistance)
                	immune = true;
                if (!immune){
                	any_sleep = true;
                    c.status_effects.put(Status_effect_code.EF_SLEEP,new Effect_parameters(duration()));
                    c.status_effects.get(Status_effect_code.EF_SLEEP).level = 3;
                    c.action_in_progress = null;
                    c.current_stance = null;
                }
            }
        }
        if (any_sleep)
	        Gamestate.add_sound("yawn");
    }

    String get_description(){
        return "Makes your whole party fall asleep. Tap them three times to make them wake up. Can be countered with the 'Counter: Sleep' spell.";
    }
}

class Flashing_gaze_MA extends Monster_action {
    public Flashing_gaze_MA(){
        super();
    }

    String name(){
        return "Roar";
    }
    
    String message(){
        return "ROAR!!!";
    }

    Double period(){
        return 5.0;
    }

    Double cooldown(){
    	return 8.0;
    }

    boolean target_enemy(){
        return true;
    }

    boolean enemy_target_back_row_ok(){
        return false;
    }

    void make_stuff_happen(){
		Gamestate.add_sound("roar");
        if (char_target.alive && !char_target.about_to_die){
		  if (Gamestate.p.n_surviving_front_chars()>1){
        	Position_code move_to = null;
            if (char_target.pos==Position_code.POS_C_FRONT_1){
                move_to = Position_code.POS_C_BACK_1;
            } else if (char_target.pos==Position_code.POS_C_FRONT_2){
                move_to = Position_code.POS_C_BACK_2;
            } else if (char_target.pos==Position_code.POS_C_FRONT_3){
                move_to = Position_code.POS_C_BACK_3;
            }
            Character displaced = Gamestate.p.get_char_at_pos(move_to);
            if (displaced!=null){
                if (Gamestate.p.last_free_back_pos()!=null){
                    displaced.slide_from = displaced.pos;
                    displaced.pos = Gamestate.p.last_free_back_pos();
                    displaced.slide_to = displaced.pos;
                    displaced.slide_start = Gamestate.w.clock;
                    displaced.slide_finish = Gamestate.w.clock + 0.1;
                } else {
                    displaced.slide_from = displaced.pos;
                    displaced.pos = char_target.pos;
                    displaced.slide_to = displaced.pos;
                    displaced.slide_start = Gamestate.w.clock;
                    displaced.slide_finish = Gamestate.w.clock + 0.1;
                }
            }
            char_target.slide_from = char_target.pos;
            char_target.pos = move_to;
            char_target.slide_to = move_to;
            char_target.slide_start = Gamestate.w.clock;
            char_target.slide_finish = Gamestate.w.clock + 0.1;
	       }
    		boolean immune = char_target.has_status_effect(Status_effect_code.EF_MEDITATION);
    		for (Character_action a : char_target.actions){
    			if (a.action_code==Character_action_code.CA_STUN_RESISTANCE){
    				immune = true;
    				continue;
    			}
    		}
    		if (!immune){
    			char_target.status_effects.put(Status_effect_code.EF_STUN,new Effect_parameters(Gamestate.w.randint(5, 10)));
    	        char_target.action_in_progress = null;
    	        char_target.current_stance = null;
    		}
        }
    }

    String get_description(){
        return "Stuns one of your characters and pushes them to the back row.";
    }
}

class Spell_big_drain_MA extends Monster_Spell_MA {
    public Spell_big_drain_MA(){
        super();
    }

    String name(){
        return "Energy Drain";
    }

    Double period(){
        return 8.0;
    }

    boolean target_enemy(){
        return true;
    }

    void make_stuff_happen(){
        if (!char_target.draining_resistance){
	        Gamestate.add_sound("exhale");
        	char_target.energy = Math.max(0,char_target.energy - Gamestate.w.randint(10,40));
        } else {
	        Gamestate.add_sound("cancel");
        	Gamestate.add_message("Resists draining!",char_target.image,Color.WHITE);
        }
    }

    boolean draining(){
    	return true;
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_DRAINING){
    	    	Gamestate.add_sound("fail");
                c.current_stance = null;
                m.countered_at = Gamestate.w.clock;
                return true;
            }
        }
        return false;
    }

    String get_description(){
        return "Drains the energy bar of one of your characters. Can be countered with the 'Counter: Draining' spell.";
    }
}

class Gaze_Drain_Bar_MA extends Monster_action {
    public Gaze_Drain_Bar_MA(){
        super();
    }

    String name(){
        return "Life Drain";
    }

    String warning_message(){
        return "Gazes at you";
    }

    Double period(){
        return 5.0;
    }

    Double delay(){
        return 3.5;
    }

    boolean target_enemy(){
        return true;
    }

    void make_stuff_happen(){
        boolean immune = char_target.draining_resistance;
        for (Character_action a : char_target.actions){
            if (a.action_code==Character_action_code.CA_STEELY_GAZE){
                immune = true;
                continue;
            }
        }
        if (!immune){
	        Gamestate.add_sound("exhale");
        	char_target.energy = Math.max(0,char_target.energy - Gamestate.w.randint(6,18));
        } else {
	        Gamestate.add_sound("cancel");
        	Gamestate.add_message("Resists draining!",char_target.image,Color.WHITE);
        }
    }

    boolean draining(){
    	return true;
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_DRAINING){
    	    	Gamestate.add_sound("fail");
                c.current_stance = null;
                m.countered_at = Gamestate.w.clock;
                return true;
            }
        }
        return false;
    }

    String get_description(){
        return "Drains the energy bar of one of your characters. Can be countered with the 'Counter: Draining' spell.";
    }
}

class Gaze_Paralysis_Often_MA extends Gaze_Paralysis_MA {
    public Gaze_Paralysis_Often_MA(){
        super();
    }

    Double period(){
        return 2.0;
    }

    Double cooldown(){
        return 3.5;
    }

    Double delay(){
        return 2.7;
    }

    Double gc(){
        return 2.0;
    }
}

class Spit_MA extends Monster_action {
    public Spit_MA(){
        super();
    }

    String name(){
        return "Spit";
    }

    String warning_message(){
        return "Hoick...";
    }

    Double delay(){
        return 2.5;
    }

    Double period(){
        return 8.0;
    }

    Double gc(){
        return 3.0;
    }

    boolean target_enemy(){
        return true;
    }

    boolean deflectable(){
        return true;
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_DEFLECT){
    	    	Gamestate.add_sound("deflect");
                c.current_stance = null;
                return true;
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_MANY){
                c.current_stance = null;
                if (Gamestate.w.randint(1,100) <= Math.min(70,success_chance(12-m.level))){
                    return true;
                } else {
                    Gamestate.add_message("Distraction failed",c.image,Color.WHITE);
                }
            }
        }
        return false;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("spit");
        if (char_target.has_status_effect(Status_effect_code.EF_RESIST_POISON) || char_target.poison_resistance)
            return;
        int poison_level;
        if (char_target.has_status_effect(Status_effect_code.EF_POISON)){
            poison_level = Math.min(3,char_target.status_effects.get(Status_effect_code.EF_POISON).level + 2);
        } else {
            poison_level = 2;
        }
        char_target.status_effects.put(Status_effect_code.EF_POISON,new Effect_parameters(10));
        char_target.status_effects.get(Status_effect_code.EF_POISON).level = poison_level;
    }

    String get_description(){
        return "When it starts to spit at you, tell one of your front-row characters to use the Deflect ability or one of your characters will get poisoned. If you do get hit, use Cure Poison.";
    }
}

class Smite_Dark_Elf_MA extends Smite_MA {
    public Smite_Dark_Elf_MA(){
        super();
    }

    String warning_message(){
        return "Readies his blade";
    }

    Double delay(){
        return 1.8;
    }

    Double period(){
        return 5.0;
    }

    int min_damage(){
        return 20;
    }

    int max_damage(){
        return 40;
    }

    String get_description(){
        return "When he prepares to attack, tell one of your front-row characters to use the Block ability or you will get hit.";
    }

    String sound(){
    	return "sword";}
}

class Smite_MA extends Monster_action {
    // Orc warrior version;
    public Smite_MA(){
        super();
    }

    String name(){
        return "Smite";
    }

    String sound(){
    	return "smite";
    }

    String warning_message(){
        return "RAR!";
    }

    Double delay(){
        return 3.0;
    }

    Double period(){
        return 10.0;
    }

    Double gc(){
        return delay();
    }

    boolean back_row_ok(){
        return false;
    }

    boolean target_enemy(){
        return true;
    }

    boolean enemy_target_back_row_ok(){
        return false;
    }

    boolean blockable(){
        return true;
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_BLOCK){
    	    	Gamestate.add_sound("block");
                c.current_stance = null;
                return true;
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_LEAP_ASIDE){
    	    	Gamestate.add_sound("whoosh");
                c.current_stance = null;
                return true;
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_EPIC_BLOCK){
    	    	Gamestate.add_sound("block");
                return true;
            }
            if ((c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_MANY)){
                c.current_stance = null;
                if (Gamestate.w.randint(1,100) <= Math.min(70,success_chance(12-m.level))){
                    return true;
                } else {
                    Gamestate.add_message("Distraction failed",c.image,Color.WHITE);
                }
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_RIPOSTE){
                c.current_stance = null;
                if (Gamestate.w.randint(1,100) <= success_chance(c.daring + 3 - (m.health / 50))){
        	    	Gamestate.add_sound("block");
                    c.current_stance = Stance_code.ST_COUNTER;
                    c.counter_against = m;
                    return true;
                } else {
                    Gamestate.add_message("Failed to block",c.image,Color.WHITE);
                }
            }
        }
        return false;
    }

    int min_damage(){
        return 10;
    }

    int max_damage(){
        return 20;
    }

    void make_stuff_happen(){
        Gamestate.add_sound(sound());
        int damage = Gamestate.w.randint(min_damage(),max_damage());
        char_target.take_damage(Math.max(0,damage-char_target.current_armor()));
    }

    String get_description(){
        return "When it says RAR, tell one of your front-row characters to use the Block ability or you will get hit.";
    }
}

class Stab_and_disappear_MA extends Smite_MA {
    public Stab_and_disappear_MA(){
        super();
    }

    String name(){
        return "Stab";
    }

    Double period(){
        return 1.5;
    }

    String warning_message(){
        return "";
    }

    Double delay(){
        return 0.0;
    }

    void make_stuff_happen(){
        Gamestate.add_sound(sound());
        int damage = Gamestate.w.randint(30,60);
        char_target.take_damage(Math.max(0,damage-char_target.current_armor()));
        if (char_target.alive && !char_target.about_to_die && !char_target.has_status_effect(Status_effect_code.EF_RESIST_POISON) && !char_target.poison_resistance){
        	char_target.status_effects.put(Status_effect_code.EF_POISON,new Effect_parameters(10));
        	char_target.status_effects.get(Status_effect_code.EF_POISON).level = 3;
        }
        m.remove();
    }

    String get_description(){
        return "Strike and fade away into the darkness...";
    }

    String sound(){
    	return "stab";}
}

class Smite_Troll_MA extends Smite_MA {
    public Smite_Troll_MA(){
        super();
    }

    String name(){
        return "Bite";
    }

    String get_description(){
        return "When it says RAR, tell one of your front-row characters to use the Block ability or you will get bitten.";
    }

    Double delay(){
        return 2.5;
    }

    Double period(){
        return 6.0;
    }

    String sound(){
    	return "bite";
    }

    int min_damage(){
        return 15;
    }

    int max_damage(){
        return 25;
    }
}

class Smite_Naga_MA extends Smite_MA {
    public Smite_Naga_MA(){
        super();
    }

    Double delay(){
        return 3.0;
    }

    Double period(){
        return 12.0;
    }

    int min_damage(){
        return 15;
    }

    int max_damage(){
        return 30;
    }
}

class Smite_Black_Knight_MA extends Smite_MA {
    public Smite_Black_Knight_MA(){
        super();
    }

    Double delay(){
        return 2.0;
    }

    Double period(){
        return 7.0;
    }

    Double gc(){
        return 3.0;
    }

    String warning_message(){
        return "Take that!";
    }

    String get_description(){
        return "When it says 'Take That!', tell one of your front-row characters to use the Block ability or you will get hit.";
    }

    int min_damage(){
        return 20;
    }

    int max_damage(){
        return 40;
    }

    String sound(){
    	return "sword";}
}

class Smite_Black_Ogre_MA extends Smite_MA {
    public Smite_Black_Ogre_MA(){
        super();
    }

    Double delay(){
        return 2.5;
    }

    Double period(){
        return 8.0;
    }

    int min_damage(){
        return 20;
    }

    int max_damage(){
        return 40;
    }
}

class Smite_Dark_Priest_MA extends Smite_Black_Knight_MA {
    public Smite_Dark_Priest_MA(){
        super();
    }

    Double delay(){
        return 3.0;
    }

    Double period(){
        return 12.0;
    }

    int min_damage(){
        return 15;
    }

    int max_damage(){
        return 30;
    }

    String sound(){
    	return "smite";}
}

class Smite_Berserker_MA extends Smite_MA {
    public Smite_Berserker_MA(){
        super();
    }

    Double delay(){
        return 2.0;
    }

    Double period(){
        return 5.0;
    }

    int min_damage(){
        return 20;
    }

    int max_damage(){
        return 40;
    }
}

class Smite_Ogre_MA extends Smite_MA {
    public Smite_Ogre_MA(){
        super();
    }

    Double delay(){
        return 3.0;
    }

    Double period(){
        return 6.0;
    }

    int min_damage(){
        return 15;
    }

    int max_damage(){
        return 30;
    }
}

class Smite_orc_boss_2_MA extends Smite_Orc_Boss_MA {
    public Smite_orc_boss_2_MA(){
        super();
    }

    Double period(){
        return 4.0;
    }

    int min_damage(){
        return 15;
    }

    int max_damage(){
        return 30;
    }
}

class Smite_Orc_Boss_MA extends Smite_MA {
    public Smite_Orc_Boss_MA(){
        super();
    }

    Double delay(){
        return 1.5;
    }

    Double period(){
        return 7.0;
    }

    int min_damage(){
        return 20;
    }

    int max_damage(){
        return 40;
    }
}

class Smite_Oathwraith_MA extends Smite_Orc_Boss_MA {
    public Smite_Oathwraith_MA(){
        super();
    }

    String warning_message(){
        return "Take that!";
    }

    String get_description(){
        return "When it says 'Take That!', tell one of your front-row characters to use the Block ability or you will get hit.";
    }

    String sound(){
    	return "sword";}
 }

class Smite_Orc_Champion_MA extends Smite_MA {
    public Smite_Orc_Champion_MA(){
        super();
    }

    Double delay(){
        return 2.0;
    }

    Double period(){
        return 8.0;
    }

    int min_damage(){
        return 15;
    }

    int max_damage(){
        return 30;
    }
}

class TailSwipe_MA extends Smite_MA {
    public TailSwipe_MA(){
        super();
    }

    String name(){
        return "Tail Swipe";
    }

    String warning_message(){
        return "Readies tail...";
    }

    Double delay(){
        return 2.0;
    }

    Double period(){
        return 5.0;
    }

    boolean back_row_ok(){
        return true;
    }

    boolean front_row_ok(){
        return false;
    }

    int min_damage(){
        return 10;
    }

    int max_damage(){
        return 20;
    }

    String get_description(){
        return "When it gets ready to swipe you, tell one of your front-row characters to use the Block ability or you will get hit.";
    }
}

class Claw_Often_MA extends Claw_MA {
    public Claw_Often_MA(){
        super();
    }

    Double period(){
        return 2.0;
    }

    int min_damage(){
        return 25;
    }

    int max_damage(){
        return 50;
    }
}

class Claw_MA extends Smite_MA {
    public Claw_MA(){
        super();
    }

    String name(){
        return "Claw";
    }

    Double delay(){
        return 2.0;
    }

    Double period(){
        return 3.0;
    }

    Double gc(){
        return 0.0;
    }

    int min_damage(){
        return 18;
    }

    int max_damage(){
        return 36;
    }

    String get_description(){
        return "When it gets ready to claw you, tell one of your front-row characters to use the Block ability or you will get hit.";
    }

    String sound(){
    	return "cleave";}
}

class Claw_fast_and_often_MA extends Claw_Often_MA {
    public Claw_fast_and_often_MA(){
        super();
    }

    Double delay(){
        return 1.2;
    }
}

class Shove_MA extends Monster_action {
    public Shove_MA(){
        super();
    }

    String name(){
        return "Shove";
    }

    Double period(){
        return 7.0;
    }

    Double cooldown(){
        return 5.0;
    }

    boolean back_row_ok(){
        return false;
    }

    boolean target_enemy(){
        return true;
    }

    boolean enemy_target_back_row_ok(){
        return false;
    }

    int min_damage(){
        return 6;
    }

    int max_damage(){
        return 12;
    }

    boolean ok(){
        return super.ok() && Gamestate.p.n_surviving_chars()>1;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("smite");
        int damage = Gamestate.w.randint(min_damage(),max_damage());
        char_target.take_damage(Math.max(0,damage-char_target.current_armor()));
        if (Gamestate.p.n_surviving_front_chars()<2) return;
        if (char_target.alive && !char_target.about_to_die){
        	Position_code move_to = null;
            if (char_target.pos==Position_code.POS_C_FRONT_1){
                move_to = Position_code.POS_C_BACK_1;
            } else if (char_target.pos==Position_code.POS_C_FRONT_2){
                move_to = Position_code.POS_C_BACK_2;
            } else if (char_target.pos==Position_code.POS_C_FRONT_3){
                move_to = Position_code.POS_C_BACK_3;
            }
            Character displaced = Gamestate.p.get_char_at_pos(move_to);
            if (displaced!=null){
                if (Gamestate.p.last_free_back_pos()!=null){
                    displaced.slide_from = displaced.pos;
                    displaced.pos = Gamestate.p.last_free_back_pos();
                    displaced.slide_to = displaced.pos;
                    displaced.slide_start = Gamestate.w.clock;
                    displaced.slide_finish = Gamestate.w.clock + 0.1;
                } else {
                    displaced.slide_from = displaced.pos;
                    displaced.pos = char_target.pos;
                    displaced.slide_to = displaced.pos;
                    displaced.slide_start = Gamestate.w.clock;
                    displaced.slide_finish = Gamestate.w.clock + 0.1;
                }
            }
            char_target.slide_from = char_target.pos;
            char_target.pos = move_to;
            char_target.slide_to = move_to;
            char_target.slide_start = Gamestate.w.clock;
            char_target.slide_finish = Gamestate.w.clock + 0.1;
        }
    }

    String get_description(){
        return "Pushes one of your front row characters into the back row.";
    }
}

class Tornado_MA extends Monster_action {
    public Tornado_MA(){
        super();
    }

    String name(){
        return "Tornado";
    }

    Double period(){
        return 10.0;
    }

    Double cooldown(){
        return 5.0;
    }

    Double likelihood_increase_until_n_uses(){
        return 3.0;
    }

    Integer n_uses(){
        return 1;
    }

    Double gc(){
        return 2.0;
    }

    int min_damage(){
        return 2;
    }

    int max_damage(){
        return 6;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("wind");
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die){
                c.take_damage(Gamestate.w.randint(min_damage(),max_damage()));
                c.current_stance = null;
                c.action_in_progress = null;
            }
        }
    	if (Gamestate.p.n_surviving_chars()==0) return;
        int posses_to_take = 0;
        for (Character c : Gamestate.p.chars)
            if (c.pos!=null && c.alive)
                posses_to_take = posses_to_take + 1;
        List<Position_code> posses_taken;
        List<Position_code> all_posses = Arrays.asList(Position_code.POS_C_FRONT_1,Position_code.POS_C_FRONT_2,Position_code.POS_C_FRONT_3,Position_code.POS_C_BACK_1,Position_code.POS_C_BACK_2,Position_code.POS_C_BACK_3);
        while (true){
        	Collections.shuffle(all_posses);
            posses_taken = all_posses.subList(0,posses_to_take);
            if (posses_taken.contains(Position_code.POS_C_FRONT_1) || posses_taken.contains(Position_code.POS_C_FRONT_2) || posses_taken.contains(Position_code.POS_C_FRONT_3))
                break;
        }
        int count = 0;
        for (Character c : Gamestate.p.chars){
            if (c.alive){
                c.slide_from = c.pos;
                c.slide_to = posses_taken.get(count);
                c.pos = c.slide_to;
                c.slide_start = Gamestate.w.clock;
                c.slide_finish = Gamestate.w.clock + 0.1;
                count = count + 1;
            }
        }
	    if (Gamestate.w.can_fall && Gamestate.p.n_surviving_chars()>1){
	    	for (Character c : Gamestate.p.chars){
	    		if (c.alive && !c.about_to_die){
	    			if (Gamestate.w.one_in(6)){
		    			Gamestate.add_message("Fell off the crag!",c.image,Color.WHITE);
		    			Gamestate.add_sound("falling");
		                c.slide_from = c.pos;
		                c.slide_to = Position_code.POS_DOWN;
		                c.slide_start = Gamestate.w.clock;
		                c.slide_finish = Gamestate.w.clock + 0.15;
		                boolean front_row = c.front_row();
	    	            c.pos = null;
	    	            c.alive = false;
	    	            c.about_to_die = false;
    	                if (Gamestate.p.n_surviving_chars()==0) Gamestate.quit_after_pause = true;
    	                if (front_row)
    	                    if (Gamestate.p.n_surviving_front_chars()==0)
    	                    	Gamestate.p.push_char_forward();
    	                break;
	    			}
	    		}
	    	}
	    }
	}

    String get_description(){
        return "The swirling winds push all your characters out of position, as well as damaging them. There is no defense.";
    }
}

class Enter_Shell_MA extends Monster_action {
    public Enter_Shell_MA(){
        super();
    }

    String name(){
        return "Ducks into its shell";
    }

    Double period(){
        return 6.0;
    }

    Double cooldown(){
        return 10.0;
    }

    boolean ok(){
        if (m.invulnerable_until == 99999){
            return false;
        } else {
            return super.ok();
        }
    }

    Double likelihood_increase_until_n_uses(){
        return 2.0;
    }

    Integer n_uses(){
        return 1;
    }

    void make_stuff_happen(){
        m.invulnerable_until = 99999;
        m.fire_resistant = true;
    }

    String get_description(){
        return "Protects it from most kinds of damage. It can still attack you, however, with its heavily armored forepaws.";
    }
}

class Exit_Shell_MA extends Monster_action {
    public Exit_Shell_MA(){
        super();
    }

    String name(){
        return "Emerges from its shell";
    }

    Double period(){
        if (Gamestate.e.n_surviving_mobs()==1){
            return 3.0;
        } else {
            return 6.0;
        }
    }

    boolean ok(){
        if (m.invulnerable_until == 0){
            return false;
        } else {
            return super.ok();
        }
    }

    void make_stuff_happen(){
        m.invulnerable_until = 0;
        m.fire_resistant = false;
    }

    String get_description(){
        return "Makes it briefly vulnerable to normal damage.";
    }
}

class Anticaster_MA extends Monster_action {
    public Anticaster_MA(){
        super();
    }

    String name(){
        return "Zap";
    }

    Double period(){
        return 1.0;
    }

    boolean ok(){
        for (Character c : Gamestate.p.chars)
            if (c.alive && !c.about_to_die && c.action_in_progress!=null && c.action_in_progress.disruptable())
                return super.ok();
        return false;
    }

    int min_damage(){
        return 2;
    }

    int max_damage(){
        return 6;
    }

    void make_stuff_happen(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.action_in_progress!=null && c.action_in_progress.disruptable()){
                Gamestate.add_sound("zap");
                int damage = Gamestate.w.randint(min_damage(),max_damage());
                c.take_damage(damage);
                return;
        	}
        }
    }

    String get_description(){
        return "Zaps one of your casters just as they start to cast a spell. Armor does protect you, and may even allow you to complete the spell.";
    }
}

class Firebolt_MA extends Monster_action {
    public Firebolt_MA(){
        super();
    }

    String name(){
        return "Throw Fire";
    }

    String warning_message(){
        return "Flares up";
    }

    boolean bolt(){
        return true;
    }

    Double delay(){
        return 2.0;
    }

    Double period(){
        return 6.0;
    }

    Double gc(){
        return delay()+1;
    }

    int min_damage(){
        return 10;
    }

    int max_damage(){
        return 20;
    }

    boolean fiery(){
        return true;
    }

    boolean target_enemy(){
        return true;
    }
//	    def enemy_target_back_row_ok(){
//	        return false;

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_BOLTS){
    	    	Gamestate.add_sound("fail");
                m.countered_at = Gamestate.w.clock;
                c.current_stance = null;
                return true;
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_MANY){
                c.current_stance = null;
                if (Gamestate.w.randint(1,100) <= Math.min(70,success_chance(12-m.level))){
                    return true;
                } else {
                    Gamestate.add_message("Distraction failed",c.image,Color.WHITE);
                }
            }
        }
        return false;
    }

    String sound(){
        return "fire_bolt";
    }

    void make_stuff_happen(){
        Gamestate.add_sound(sound());
        int damage = Gamestate.w.randint(min_damage(),max_damage());
        if (char_target.has_status_effect(Status_effect_code.EF_IMMUNE_FIRE))
            damage = damage / 4;
        else if (char_target.has_status_effect(Status_effect_code.EF_RESIST_FIRE) || char_target.fire_resistance)
            damage = damage / 2;
        char_target.take_damage(damage);
        char_target.flash_image = flash_image();
        char_target.flash_long = true;
    }

    String flash_image(){
    	return "fire_down";
    }

    String get_description(){
        return "Burns one of your front row characters. Armour is no protection, but fire resistance halves damage. Can be countered with the Reflection spell.";
    }
}

class Zap_MA extends Firebolt_MA {
    public Zap_MA(){
        super();
    }

    Double period(){
        return 4.0;
    }

    String name(){
        return "Zap";
    }

    Double delay(){
        return 1.0;
    }

    int min_damage(){
        return 2;
    }

    int max_damage(){
        return 6;
    }

    String sound(){
        return "zap";
    }

    String flash_image(){
    	return "missile_down";
    }
}

class Darkness_often_MA extends Darkness_MA{
    public Darkness_often_MA(){
        super();
    }

    Double period(){
        return 2.5;
    }
}

class Darkness_MA extends Monster_action {
    public Darkness_MA(){
        super();
    }

    String name(){
        return "Darkness";
    }

    Double period(){
        return 5.0;
    }

    Double likelihood_increase_until_n_uses(){
        return 3.0;
    }

    Integer n_uses(){
        return 1;
    }

    void make_stuff_happen(){
       Gamestate.add_sound("dark");
       Gamestate.w.darkness_at = Gamestate.w.clock;
       Gamestate.w.darkness = Gamestate.w.darkness + 1;
    }

    String get_description(){
        return "Darkens the screen. Stacks cumulatively, but fortunately wears off after a while - or use the Light spell.";
    }
}

class Silence_MA extends Monster_action {
    public Silence_MA(){
        super();
    }

    String name(){
        return "Silent Aura";
    }

    String warning_message(){
        return "Gestures mutely";
    }

    boolean silencing(){
        return true;
    }

    Double period(){
        return 6.0;
    }

    Double cooldown(){
        return 8.0;
    }

    Double gc(){
        return 3.0;
    }

    Double likelihood_increase_until_n_uses(){
        return 2.0;
    }

    Double delay(){
        return 2.0;
    }

    Integer n_uses(){
        return 1;
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_SILENCE){
    	    	Gamestate.add_sound("fail");
                m.countered_at = Gamestate.w.clock;
                c.current_stance = null;
                return true;
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_MANY){
                c.current_stance = null;
                if (Gamestate.w.randint(1,100) <= Math.min(70,success_chance(12-m.level))){
                    return true;
                } else {
                    Gamestate.add_message("Distraction failed",c.image,Color.WHITE);
                }
            }
        }
        return false;
    }

    void make_stuff_happen(){
       Gamestate.w.silence_until = Gamestate.w.clock + Gamestate.w.randint(6,12);
       for (Character c : Gamestate.p.chars){
           if (c.alive && !c.about_to_die && c.action_in_progress!=null && c.action_in_progress.disruptable()){
               c.action_in_progress = null;
               Gamestate.add_message("Action disrupted",c.image,Color.WHITE);
           }
       }
    }

    String get_description(){
        return "Creates a magical silence, preventing your characters from casting any spells. Can be countered with the 'Counter: Silence' spell.";
    }
}

class Breath_mini_poison_MA extends Monster_action {
    public Breath_mini_poison_MA(){
        super();
    }

    String name(){
        return "Breathes poison gas!!";
    }

    Double period(){
        return 2.0;
    }

    Double cooldown(){
        return 8.0;
    }

    boolean front_row_ok(){
        return true;
    }

    boolean back_row_ok(){
        return false;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("gas");
        Gamestate.w.poison_cloud_until = Gamestate.w.clock + 7.0;
        Gamestate.w.poison_cloud_from = null;
    }

    String get_description(){
        return "Breathes out a poisonous cloud enveloping your entire front row, which will last for some time. You had better not stand in it if you can help it, especially if you don't have poison resistance.";
    }
}

class Breath_mini_fire_MA extends Breath_Fire_MA {
    public Breath_mini_fire_MA(){
        super();
    }

    Double likelihood_increase_until_n_uses(){
        return 3.0;
    }

    Double gc(){
        return 2.0;
    }

    boolean front_row_ok(){
        return true;
    }

    boolean back_row_ok(){
        return false;
    }

    int damroll(){
        return Gamestate.w.randint(10,20);
    }

    String get_description(){
        return "Burns your entire party. Can be countered with the 'Take Cover!' ability, though this is not 100% reliable, or the 'Extinguish' spell. Fire resistance halves damage.";
    }
}

class Breath_Fire_MA extends Monster_action {
    public Breath_Fire_MA(){
        super();
    }

    String name(){
        return "Breathes fire!!";
    }

    String warning_message(){
        return "Inhales...";
    }

    Double delay(){
        return 4.0;
    }

    Double period(){
        return 10.0;
    }

    Double likelihood_increase_until_n_uses(){
        return 5.0;
    }

    Integer n_uses(){
        return 1;
    }

    Double cooldown(){
        return 8.0;
    }

    Double gc(){
        return 5.0;
    }

    boolean front_row_ok(){
        return false;
    }

    boolean fiery(){
        return true;
    }

    boolean take_coverable(){
        return true;
    }

    int damroll(){
        return Gamestate.w.randint(40,60);
    }

    void make_stuff_happen(){
       Gamestate.add_sound("breath");
       for (Character c : Gamestate.p.chars){
           if (c.alive && !c.about_to_die){
               int damage = damroll();
               if (c.current_stance==Stance_code.ST_TAKE_COVER){
                   // pass;
               } else {
            	   if (c.has_status_effect(Status_effect_code.EF_IMMUNE_FIRE))
                       damage = damage / 4;
                   else if (c.has_status_effect(Status_effect_code.EF_RESIST_FIRE) || c.fire_resistance)
                       damage = damage / 2;
                   c.take_damage(damage);
               	   c.flash_image = "fire_down";
               	   c.flash_long = true;
               }
           }
       }
    }

    String get_description(){
        return "Fries your entire party. Can be countered with the 'Take Cover!' ability, though this is not 100% reliable, or the 'Extinguish' spell. Fire resistance halves damage.";
    }
}

class Breath_Small_Fire_MA extends Breath_Fire_MA {
    public Breath_Small_Fire_MA(){
        super();
    }

    boolean back_row_ok(){
        return false;
    }

    boolean front_row_ok(){
        return true;
    }

    Double delay(){
        return 3.0;
    }

    Double period(){
        return 10.0;
    }

    Double likelihood_increase_until_n_uses(){
        return 2.0;
    }

    Double cooldown(){
        return 10.0;
    }

    Double gc(){
        return 3.0;
    }

    int damroll(){
        return Gamestate.w.randint(10,30);
    }

    void make_stuff_happen(){
       Gamestate.add_sound("breath");
       for (Character c : Gamestate.p.chars){
           if (c.alive && !c.about_to_die && c.front_row()){
               int damage = damroll();
               if (c.current_stance==Stance_code.ST_TAKE_COVER){
                   // pass
               } else {
            	   if (c.has_status_effect(Status_effect_code.EF_IMMUNE_FIRE))
                       damage = damage / 4;
                   else if (c.has_status_effect(Status_effect_code.EF_RESIST_FIRE) || c.fire_resistance)
                       damage = damage / 2;
                   c.take_damage(damage);
               }
           }
       }
    }

    String get_description(){
        return "Fries all your front-row characters. Can be countered with the 'Take Cover!' ability, though this is not 100% reliable, or the 'Extinguish' spell. Fire resistance halves damage.";
    }
}

class Flit_MA extends Monster_action {
    public Flit_MA(){
        super();
    }

    Double period(){
        return 3.0;
    }

    Double cooldown(){
        return 1.0;
    }

//	    def front_row_ok(){
//	        return false;

    boolean ok(){
        if ((!m.front_row()) && Gamestate.e.last_free_back_pos()==null)
            return false;
        if (m.front_row() && Gamestate.e.last_free_front_pos()==null)
            return false;
        return super.ok();
    }

    void make_stuff_happen(){
        if (Gamestate.e.last_free_back_pos()!=null){
            Position_code slide_to = null;
            int count = 0;
            while (slide_to==null && count <= 3){
                if (m.front_row()){
                    slide_to = Gamestate.e.random_free_front_pos();
                } else {
                    slide_to = Gamestate.e.random_free_back_pos();
                }
                if (!slide_to.adjacent(m.pos))
                    slide_to = null;
                count = count + 1;
            }
            if (slide_to!=null){
                m.slide_from = m.pos;
                m.pos = slide_to;
                m.slide_to = m.pos;
                m.slide_start = Gamestate.w.clock;
                m.slide_finish = Gamestate.w.clock + 0.1;
            }
        }
    }
}

class Flit_Often_MA extends Flit_MA {
    public Flit_Often_MA(){
        super();
    }

    Double period(){
        return 1.5;
    }
}

class Flit_Taunt_MA extends Monster_action {
    public Flit_Taunt_MA(){
        super();
    }

    Double period(){
        return 3.0;
    }

    Double cooldown(){
        return 1.0;
    }

    boolean front_row_ok(){
        return false;
    }

    boolean ok(){
        if (Gamestate.e.last_free_back_pos()==null)
            return false;
        return super.ok();
    }

    List<String> messages(){
        return Arrays.asList("Arrk!","Screech!","You lousy wretch!","You'll feel my claws soon!","I'll pick your corpse!");
    }

    void make_stuff_happen(){
        if (Gamestate.e.last_free_back_pos()!=null){
            Position_code slide_to = null;
            int count = 0;
            while (slide_to==null && count <= 3){
                slide_to = Gamestate.e.random_free_back_pos();
                if (!slide_to.adjacent(m.pos))
                    slide_to = null;
                count = count + 1;
            }
            if (slide_to!=null){
                m.slide_from = m.pos;
                m.pos = slide_to;
                m.slide_to = m.pos;
                m.slide_start = Gamestate.w.clock;
                m.slide_finish = Gamestate.w.clock + 0.1;
                String message = messages().get(Gamestate.w.randint(0,messages().size()-1));
                Gamestate.add_message("'"+message+"'",m.image,Color.WHITE);
            }
        }
    }
}

class Dwarf_Spirits_MA extends Monster_action {
    public Dwarf_Spirits_MA(){
        super();
    }

    String name(){
        return "Quaffs Dwarf Spirits";
    }

    Double period(){
        return 12.0;
    }

    Double cooldown(){
        return 6.0;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("potion");
        m.health = Math.min(m.max_health,m.health + Gamestate.w.randint(5,15));
    }

    String get_description(){
        return "Heals the dipsomaniac Dwarf.";
    }
}

class Soft_Enrage_MA extends Monster_action {
    public Soft_Enrage_MA(){
        super();
    }

    String name(){
        return "Rage";
    }
    
    String message(){
        return "RAGE!!!";
    }

    Double period(){
        return 6.0;
    }

    Double cooldown(){
        return 4.0;
    }

    boolean fiery(){
        return true;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("breath");
        m.health = Math.min(m.max_health,m.health + Gamestate.w.randint(30,50));
        m.enrage = m.enrage + 1;
    }

    String get_description(){
        return "Heals itself and increases its damage. Kill it before it enrages too many times and becomes unstoppable! Or use the 'Extinguish' spell.";
    }
}

class Flee_MA extends Monster_action {
    public Flee_MA(){
        super();
    }

    Double period(){
        return 5.0;
    }

    void make_stuff_happen(){
        if (Gamestate.w.clock > (m.arrived + 20))
            m.flee();
    }
}

class Flee_Fast_MA extends Monster_action {
    public Flee_Fast_MA(){
        super();
    }

    Double period(){
        return 5.0;
    }

    void make_stuff_happen(){
        if (Gamestate.w.clock > (m.arrived + 5))
            m.flee();
    }
}

class Flee_Slowly_MA extends Monster_action {
    public Flee_Slowly_MA(){
        super();
    }

    Double period(){
        return 25.0;
    }

    void make_stuff_happen(){
        if (Gamestate.w.clock > (m.arrived + 40))
            m.flee();
    }
}

class Irish_MA extends Monster_action {
	ArrayList<String> already;

    public Irish_MA(){
        super();
        already = new ArrayList<String>();
    }

    Double period(){
        return 9.0;
    }

    Double likelihood_increase_until_n_uses(){
        return 3.0;
    }

    Double cooldown(){
        return 3.0;
    }

    Integer n_uses(){
        return 3;
    }

    List<String> messages(){
        return Arrays.asList("Faith and begorrah!","Grand altogether!","Top 'o the morning!","Ye'll niver get me gold!","Slainte!");
    }

    void make_stuff_happen(){
        String message = messages().get(Gamestate.w.randint(0,messages().size()-1));
        if (!already.contains(message)){
	        Gamestate.add_message("'"+message+"'",m.image,Color.WHITE);
	        already.add(message);
        }
    }
}

class Angst_MA extends Irish_MA {
	ArrayList<String> already;

    public Angst_MA(){
        super();
        already = new ArrayList<String>();
    }

    List<String> messages(){
        return Arrays.asList("'Man delights not me, nor woman neither.'","'Death is peaceful, life is harder.'","'Oh that this too, too solid flesh would melt!'","'I like the night. Without the dark, we'd never see the stars.'","'What if I'm not a superhero. What if I'm the bad guy?'","'The slings and arrows of outrageous fortune...'","'Believe me, my head is the last place you'd wanna be.'","'The human race is a monotonous affair.'","'I wait for the morning of my tears.'","'Do you ever feel you aren't the person you're supposed to be?'","'I'm hoping he can save me, even though I realise he can't.'","'Deep into that darkness peering, long I stood there.'","'Beauty of whatever kind, in its supreme development, invariably excites the sensitive soul to tears.'","'I'm just a soul whose intentions are good...'");
    }
}

class Argot_MA extends Irish_MA {
	ArrayList<String> already;

    public Argot_MA(){
        super();
        already = new ArrayList<String>();
    }

    List<String> messages(){
        return Arrays.asList("I'll cut your painter for ye!","I'll lend ye a polt in the muns!","Sowr the cull!","I'll tip him a jordain!","Let's kimbaw the cove!","Away to the boozing-ken!","I'll file a cly!","The swell is well breeched!");
    }
}

class Burns_MA extends Scots_MA {
	ArrayList<String> already;

    public Burns_MA(){
        super();
        already = new ArrayList<String>();
    }

    List<String> messages(){
        return Arrays.asList("A dwarf's a dwarf for a' that","The best laid schemes o' mice and men gang aft agley","Oh wad some power the giftie gie us!","Some hae meat and canna eat...","O, what a panic's in thy breastie!","Gie me ae spark o' Nature's fire!","I maun crush thy slender stem!","Welcome to your gory bed!","Nae dwarf can tether time or tide...","In hell they'll roast you like a herrin'!");
    }
}

class Scots_MA extends Irish_MA {
	ArrayList<String> already;

    public Scots_MA(){
        super();
        already = new ArrayList<String>();
    }

    Double period(){
        return 18.0;
    }

    Double likelihood_increase_until_n_uses(){
        return 3.0;
    }

    Integer n_uses(){
        return 3;
    }

    List<String> messages(){
        return Arrays.asList("I'll gie ye a skelpit lug!","Ye wee bampot!","Ah'm no fooling ye!","Here's a Glasgow kiss fer ye!","Yese a minger, so ye is!","Ah, Goad! Ah'm steamin");
    }
}

class Leap_Away_MA extends Fly_MA {
    public Leap_Away_MA(){
        super();
    }

    String name(){
        return "Leap Away";
    }

    Double period(){
        return 0.2;
    }

    double time_back(){
    	return 8.0;
    }

    Double cooldown(){
    	return 11.0;
    }

    boolean ok(){
    	if (!super.ok()) return false;
    	for (Character c : Gamestate.p.chars){
    		if (c.alive && !c.about_to_die && c.action_in_progress!=null && c.action_in_progress.mob_target==m) return true;
    	}
    	return false;
    }

    String sound(){
    	return "whoosh";}
}

class Summon_golems_instantly_and_meteor_swarm_later_MA extends Monster_action {
	public Summon_golems_instantly_and_meteor_swarm_later_MA(){
		super();
	}

	String message(){
		return "Summon Golems!";
	}

    Double period(){
        return 0.2;
    }

    Double cooldown(){
        return 9999.0;
    }

    void make_stuff_happen(){
        if (m.front_row() && Gamestate.e.last_free_back_pos()!=null){
            m.slide_from = m.pos;
            m.pos = Gamestate.e.last_free_back_pos();
            m.slide_to = m.pos;
            m.slide_start = Gamestate.w.clock;
            m.slide_finish = Gamestate.w.clock + 0.1;
        }
        for (int i=0; i<2; i++){
            Mob new_critter = Gamestate.e.add_mob(Monster_code.MOB_STONE_GOLEM);
            if (new_critter!=null){
                new_critter.slide_from = null;
                new_critter.fade_in = true;
                new_critter.fade_start = Gamestate.w.clock;
                new_critter.fade_finish = Gamestate.w.clock + 0.75;
                new_critter.summoner = m;
            }
        }
        Gamestate.add_sound("summon");
        if (!m.front_row() && Gamestate.e.n_surviving_front_mobs()==0){
            m.slide_from = m.pos;
            m.pos = Gamestate.e.last_free_front_pos();
            m.slide_to = m.pos;
            m.slide_start = Gamestate.w.clock;
            m.slide_finish = Gamestate.w.clock + 0.1;
        }
    	Gamestate.w.meteors_at.add(Gamestate.w.clock + 60.0);
    	Gamestate.w.meteors_at.add(Gamestate.w.clock + 64.0);
    	Gamestate.w.meteors_at.add(Gamestate.w.clock + 68.0);
    	Gamestate.w.meteors_at.add(Gamestate.w.clock + 72.0);
    	Gamestate.w.meteors_at.add(Gamestate.w.clock + 76.0);
    }
}

class Meteor_swarm_wait_summon_undeads_flee_MA extends Monster_action {
    public Meteor_swarm_wait_summon_undeads_flee_MA(){
        super();
    }

    Double period(){
        return 2.0;
    }

    String name(){
    	return "Meteor Swarm";
    }

    String message(){
    	return "Meteor Swarm!";
    }

    Double cooldown(){
        return 9999.9;
    }

    boolean front_row_ok(){
    	return false;
    }

    void make_stuff_happen(){
    	Gamestate.w.meteors_at.add(Gamestate.w.clock + 2.0);
    	Gamestate.w.meteors_at.add(Gamestate.w.clock + 6.0);
    	Gamestate.w.meteors_at.add(Gamestate.w.clock + 10.0);
    	Gamestate.w.meteors_at.add(Gamestate.w.clock + 14.0);
    	Gamestate.w.meteors_at.add(Gamestate.w.clock + 18.0);
    	Gamestate.w.lich_summon_at = Gamestate.w.clock + 25.0;
    	Gamestate.w.lich_flee_at = Gamestate.w.clock + 27.0;
    }

    String get_description(){
        return "Five glowing meteors fall from the sky and crash to earth amongst your party! The Meteor Swarm is the most powerful spell in the game, doing both impact and fire damage. The 'Leap Aside' or 'Forcefield' abilities can protect a character from the impact damage, if timed right. 'Take Cover' is one defense against the fire damage.";
    }
}

class Always_fly_and_leave_later_MA extends Always_fly_MA {
    public Always_fly_and_leave_later_MA(){
        super();
    }

    void make_stuff_happen(){
    	super.make_stuff_happen();
    	Gamestate.w.lich_flee_at = Gamestate.w.clock + 30.0;
    }
}

class Always_fly_MA extends Fly_MA {
    public Always_fly_MA(){
        super();
    }

    Double period(){
        return 0.1;
    }

    double time_back(){
    	return 9999.0;
    }

    Double cooldown(){
    	return 1.0;
    }
}

class Fly_MA extends Monster_action {
    public Fly_MA(){
        super();
    }

    Double period(){
        return 7.0;
    }

    Double likelihood_increase_until_n_uses(){
        return 2.0;
    }

    double time_back(){
    	return 15.0;
    }

    Integer n_uses(){
        return 1;
    }

    Double gc(){
        return 1.0;
    }

    boolean back_row_ok(){
        return false;
    }

    Double cooldown(){
    	return 20.0;
    }

    String sound(){
    	return "fly";}

    void make_stuff_happen(){
        if (Gamestate.e.last_free_back_pos()==null)
            Gamestate.e.push_monster_forward(m.level);
        if (Gamestate.e.last_free_back_pos()!=null){
    		Gamestate.add_sound(sound());
            m.slide_from = m.pos;
            m.pos = Gamestate.e.random_free_back_pos();
            m.slide_to = m.pos;
            m.slide_start = Gamestate.w.clock;
            m.slide_finish = Gamestate.w.clock + 0.1;
            m.move_to_front_at = Gamestate.w.clock + time_back();
        }
    }
}

class Fly_Often_MA extends Fly_MA {
    public Fly_Often_MA(){
        super();
    }

    Double period(){
        return 4.0;
    }

    double time_back(){
    	return 5.0;
    }
    
    Double gc(){
        return 0.0;
    }

    Double cooldown(){
    	return 6.0;
    }
}

class Advance_MA extends Monster_action {
    public Advance_MA(){
        super();
    }

    Double period(){
        return 5.0;
    }

    boolean front_row_ok(){
        return false;
    }

    boolean ok(){
        if (Gamestate.e.last_free_front_pos()==null){
            return false;
        } else {
            return super.ok();
        }
    }

    void make_stuff_happen(){
        if (Gamestate.e.last_free_front_pos()!=null){
            m.slide_from = m.pos;
            m.pos = Gamestate.e.random_free_front_pos();
            m.slide_to = m.pos;
            m.slide_start = Gamestate.w.clock;
            m.slide_finish = Gamestate.w.clock + 0.1;
        }
    }
}

class Hurl_And_Advance_MA extends Monster_action {
    public Hurl_And_Advance_MA(){
        super();
    }

    String name(){
        return "Hurl Boulder";
    }

    String warning_message(){
        return "Picks up a big rock";
    }

    Double delay(){
        return 2.0;
    }

    Double period(){
        return 3.0;
    }

    Double cooldown(){
        return 10.0;
    }

    Double likelihood_increase_until_n_uses(){
        return 2.0;
    }

    Integer n_uses(){
        return 1;
    }

    boolean front_row_ok(){
        return false;
    }

    boolean take_coverable(){
        return true;
    }

    boolean leap_awayable(){
        return true;
    }

    void make_stuff_happen(){
        m.move_to_front_at = Gamestate.w.clock + 1;
        for (Character c : Gamestate.p.chars){
			if (c.alive && !c.about_to_die && c.current_stance!=null & c.current_stance==Stance_code.ST_CATCH){
				Gamestate.add_message("Caught the rock!",c.image,Color.WHITE);
				c.take_damage(Gamestate.w.randint(5, 15));
				return;
			}
		}
        Character target = Gamestate.p.random_front_target(m);
        if (target!=null){
            if (target.current_stance==Stance_code.ST_LEAP_ASIDE){
                target.current_stance = null;
                Gamestate.add_sound("whoosh");
                Gamestate.add_message("Missed!",m.image,Color.WHITE);
                return;
            }
            if (target.current_stance==Stance_code.ST_TAKE_COVER){
                for (Character c : Gamestate.p.chars)
                    c.current_stance = null;
				Gamestate.add_sound("monmiss");
				Gamestate.add_message("Missed!",m.image,Color.WHITE);
                return;
            }
            if (Gamestate.w.randint(1,20)<=2*(8 - target.current_evasion())){
                Gamestate.add_sound("thump");
                int damage = Gamestate.w.randint(20,50);
                int armor = target.current_armor();
                Gamestate.add_message("Ouch!",target.image,Color.WHITE);
                if (damage > armor)
                    target.take_damage(Math.max(0,damage-armor));
                target.flash_image="boulder";
                target.flash_long = true;
            } else {
                Gamestate.add_message("Missed!",m.image,Color.WHITE);
                Gamestate.add_sound("monmiss");
            }
        }
    }

    String get_description(){
        return "Throws a heavy rock at you and then advances to the front row. The rock can be countered with the 'Leap Aside' ability - or the 'Take Cover!' ability, though this is not reliable.";
    }
}

class Hurl_MA extends Hurl_And_Advance_MA {
    public Hurl_MA(){
        super();
    }

    Double period(){
        return 8.0;
    }

    Double cooldown(){
        return 1.0;
    }

    void make_stuff_happen(){
    	super.make_stuff_happen();
        m.move_to_front_at = 0;
    }
    String get_description(){
        return "Throws a heavy rock at you. The rock can be countered with the 'Leap Aside' ability - or the 'Take Cover!' ability, though this is not reliable.";
    }
}

class Swoop_MA extends Monster_action {
    public Swoop_MA(){
        super();
    }

    Double period(){
        return 5.0;
    }

    boolean front_row_ok(){
        return false;
    }

    boolean ok(){
        if (Gamestate.e.last_free_front_pos()==null){
            return false;
        } else {
            return super.ok();
        }
    }

    void make_stuff_happen(){
        if (Gamestate.e.last_free_front_pos()!=null){
            Gamestate.add_sound("fly");
            m.slide_from = m.pos;
            m.pos = Gamestate.e.random_free_front_pos();
            m.slide_to = m.pos;
            m.slide_start = Gamestate.w.clock;
            m.slide_finish = Gamestate.w.clock + 0.1;
            Character target = Gamestate.p.random_front_target(m);
            if (target!=null){
                if (m.melee_hit_roll(target)){
                    Gamestate.add_sound(m.melee_hit_sound);
                    int damage = m.melee_damage_roll();
                    int armor = target.current_armor();
                    if (damage > armor)
                        target.take_damage(Math.max(0,damage-armor));
                } else {
                    Gamestate.add_sound(m.melee_miss_sound);
                }
            }
            m.move_to_back_at = Gamestate.w.clock + 4;
        }
    }

    String get_description(){
        return "Can move forwards, attack you immediately, and soon return to the back row.";
    }
}

class Earth_shock_MA extends Monster_action {
    public Earth_shock_MA(){
        super();
    }

    String name(){
        return "Earth Shock";
    }

    Double period(){
        return 0.5;
    }

    boolean ok(){
    	if (!super.ok()) return false;
    	for (Character c : Gamestate.p.chars){
    		if (c.alive && !c.about_to_die && c.action_in_progress!=null && c.action_in_progress.mob_target==m) return true;
    	}
    	return false;
    }

    Double cooldown(){
        return 15.0;
    }

    void make_stuff_happen(){
       Gamestate.add_sound("earthshock");
       for (Character c : Gamestate.p.chars){
           if (c.alive && !c.about_to_die){
	           c.take_damage(Gamestate.w.randint(5,10));
	           if (c.alive && !c.about_to_die){
					boolean immune = false;
					for (Character_action a : c.actions){
						if (a.action_code==Character_action_code.CA_STUN_RESISTANCE){
							immune = true;
							continue;
						}
					}
					if (!immune){
						c.status_effects.put(Status_effect_code.EF_STUN,new Effect_parameters(2.0));
						c.action_in_progress = null;
					}
               }
           }
       }
    }

    String get_description(){
        return "This small earthquake damages all your characters and interrupts whatever you were doing.";
    }
}

class Explode_MA extends Monster_action {
    public Explode_MA(){
        super();
    }

    String name(){
        return "Explode!!";
    }

    Double period(){
        return 2.0;
    }

    Double gc(){
        return 2.0;
    }

    boolean back_row_ok(){
        return false;
    }

    boolean take_coverable(){
        return true;
    }

    void make_stuff_happen(){
       Gamestate.add_sound("bang");
       for (Character c : Gamestate.p.chars){
           if (c.alive && !c.about_to_die && c.front_row()){
               if (c.current_stance==Stance_code.ST_TAKE_COVER){
                   // pass;
               } else {
                   c.take_damage(Gamestate.w.randint(10,20));
                   if (c.alive && !c.about_to_die){
						boolean immune = false;
						for (Character_action a : c.actions){
							if (a.action_code==Character_action_code.CA_STUN_RESISTANCE){
								immune = true;
								continue;
							}
						}
						if (!immune){
							c.status_effects.put(Status_effect_code.EF_STUN,new Effect_parameters(2.0));
							c.action_in_progress = null;
						}
				   }
               }
           }
       }
       m.die();
    }

    String get_description(){
        return "Damages your whole front row. Can be countered with the 'Take Cover!' ability, though this is not 100% reliable. Or just kill it before it explodes!";
    }
}

class Aura_Fire_MA extends Monster_action {
    public Aura_Fire_MA(){
        super();
    }

    String name(){
        return "Fiery Aura";
    }

    Double period(){
        return 3.0;
    }

    Double cooldown(){
        return 2.0;
    }

    boolean back_row_ok(){
        return false;
    }

    boolean fiery(){
        return true;
    }

    int min_damage(){
        return 4;
    }

    int max_damage(){
        return 8;
    }

    void make_stuff_happen(){
    	Gamestate.add_sound("small fire");
        for (Position_code pos : Arrays.asList(Position_code.POS_C_FRONT_1,Position_code.POS_C_FRONT_2,Position_code.POS_C_FRONT_3)){
            Character c = Gamestate.p.get_char_at_pos(pos);
            if (c!=null && c.alive && !c.about_to_die){
                int damage = Gamestate.w.randint(min_damage(),max_damage());
                if (c.has_status_effect(Status_effect_code.EF_IMMUNE_FIRE))
                    damage = damage / 4;
                else if (c.has_status_effect(Status_effect_code.EF_RESIST_FIRE) || c.fire_resistance)
                    damage = damage/2;
                c.take_damage(damage);
            }
        }
        for (Position_code pos : Arrays.asList(Position_code.POS_M_FRONT_1,Position_code.POS_M_FRONT_2,Position_code.POS_M_FRONT_3)){
            Mob mm = Gamestate.e.get_monster_at_pos(pos);
            if (mm!=null && mm.alive && !mm.about_to_die && !mm.fire_resistant){
                int damage = Gamestate.w.randint(min_damage(),max_damage());
                mm.take_damage(damage);
            }
        }
        m.flash_image="red";
    }

    String get_description(){
        return "Burns all front-row characters. Cannot be prevented, though fire resistance helps. Can also damage front-row monsters.";
    }
}

class Aura_Fire_Rage_MA extends Aura_Fire_MA{
    public Aura_Fire_Rage_MA(){
        super();
    }

    Double period(){
        return Math.max(2.0,5.0-m.enrage);
    }

    int min_damage(){
        return 2 + m.enrage;
    }

    int max_damage(){
        return 2 * min_damage();
    }
}

class Aura_Cold_MA extends Aura_Fire_MA {
    public Aura_Cold_MA(){
        super();
    }

    String name(){
        return "Frigid Aura";
    }

    boolean fiery(){
        return false;
    }

    boolean freezy(){
        return true;
    }

    int damroll(){
        return Gamestate.w.randint(4,8);
    }

    void make_stuff_happen(){
    	Gamestate.add_sound("wind");
    	for (Position_code pos : Arrays.asList(Position_code.POS_C_FRONT_1,Position_code.POS_C_FRONT_2,Position_code.POS_C_FRONT_3)){
            Character c = Gamestate.p.get_char_at_pos(pos);
            if (c!=null && c.alive && !c.about_to_die){
                int damage = damroll();
                if (c.has_status_effect(Status_effect_code.EF_RESIST_COLD) || c.cold_resistance)
                    damage = damage/2;
                c.take_damage(damage);
            }
        }
        for (Position_code pos : Arrays.asList(Position_code.POS_M_FRONT_1,Position_code.POS_M_FRONT_2,Position_code.POS_M_FRONT_3)){
            Mob mm = Gamestate.e.get_monster_at_pos(pos);
            if (mm!=null && mm.alive && !mm.about_to_die && !mm.cold_resistant)
                mm.take_damage(damroll());
        }
        m.flash_image="light_blue";
    }

    String get_description(){
        return "Damages all front-row characters. Cannot be prevented. Can also damage front-row monsters.";
    }
}

class Aura_Cool_MA extends Aura_Cold_MA {
    public Aura_Cool_MA(){
        super();
    }

    Double period(){
        return 4.0;
    }

    int damroll(){
        return Gamestate.w.randint(2,3);
    }
}

class Epic_block_MA extends Monster_action {
    public Epic_block_MA(){
        super();
    }

    String name(){
        return "Epic Block";
    }

    Double cooldown(){
    	return 5.0;
    }

    boolean ok(){
        for (Character c : Gamestate.p.chars)
            if (c.alive && !c.about_to_die && c.action_in_progress!=null && (c.action_in_progress.action_code==Character_action_code.CA_CLEAVE || c.action_in_progress.action_code==Character_action_code.CA_SUPER_SMITE))
                return super.ok();
        return false;
    }

    Double period(){
        return 0.1;
    }

    void make_stuff_happen(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.action_in_progress!=null){
            	if (c.action_in_progress.action_code==Character_action_code.CA_CLEAVE || c.action_in_progress.action_code==Character_action_code.CA_SUPER_SMITE){
            		if (!Gamestate.w.one_in(3)){
            	    	Gamestate.add_sound("block");
		                c.action_in_progress = null;
		                return;
            		}
            	}
            }
        }
    }

    boolean back_row_ok(){
        return false;
    }

    String get_description(){
        return "Blocks your Super Smite and Cleave attacks.";
    }
}

class Block_MA extends Monster_action {
    public Block_MA(){
        super();
    }

    String name(){
        return "Block";
    }

    boolean ok(){
        for (Character c : Gamestate.p.chars)
            if (c.alive && !c.about_to_die && c.action_in_progress!=null && (c.action_in_progress.action_code==Character_action_code.CA_SMITE || c.action_in_progress.action_code==Character_action_code.CA_BASH || c.action_in_progress.action_code==Character_action_code.CA_FLURRY || c.action_in_progress.action_code==Character_action_code.CA_SUPER_SMITE))
                return super.ok();
        return false;
    }

    Double period(){
        return 0.1;
    }

    void make_stuff_happen(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.action_in_progress!=null){
            	if (c.action_in_progress.action_code==Character_action_code.CA_SMITE || c.action_in_progress.action_code==Character_action_code.CA_BASH || c.action_in_progress.action_code==Character_action_code.CA_FLURRY){
        	    	Gamestate.add_sound("block");
	                c.action_in_progress = null;
	                return;
            	}
            	if (c.action_in_progress.action_code==Character_action_code.CA_SUPER_SMITE){
            		if (Gamestate.w.one_in(2)){
            	    	Gamestate.add_sound("block");
            			c.action_in_progress = null;
            		} else {
            			m.stunned = true;
            			m.stunned_until = Gamestate.w.clock + 2;
            		}
        			return;
            	}
            }
        }
    }

    boolean back_row_ok(){
        return false;
    }

    String get_description(){
        return "Blocks your Bash and Smite attacks.";
    }
}

class Catch_MA extends Monster_action {
    public Catch_MA(){
        super();
    }

    String name(){
        return "Hook";
    }
    
    String message(){
        return "Hooks you!";
    }

    boolean ok(){
        for (Character c : Gamestate.p.chars)
            if (c.alive && !c.about_to_die && c.grappled_by==m)
                return false;
        return super.ok();
    }

    Double period(){
        return 7.0;
    }

    Double cooldown(){
        return 7.0;
    }

    Double gc(){
        return 2.0;
    }

    boolean target_enemy(){
        return true;
    }

    boolean enemy_target_back_row_ok(){
        return false;
    }

    boolean back_row_ok(){
        return false;
    }

    double duration(){
        return (double)(Gamestate.w.randint(6,12));
    }

    Status_effect_code sec(){
    	return Status_effect_code.EF_GRAPPLE;
    }

    void make_stuff_happen(){
        if (Gamestate.w.randint(1,100)<=success_chance(char_target.current_evasion())){
            Gamestate.add_sound("whoosh");
            Gamestate.add_message("Avoids the grappling attack!",char_target.image,Color.WHITE);
        } else {
            Gamestate.add_sound("grapple");
            if (char_target.alive && !char_target.about_to_die){
            	char_target.status_effects.put(sec(),new Effect_parameters(duration()));
            	char_target.action_in_progress = null;
            	char_target.current_stance = null;
            	char_target.grappled_by = m;
            }
        }
    }

    String get_description(){
        return "One of your front row characters is caught, cannot move away, and is vulnerable to the Deathblow attack.";
    }
}

class Grapple_MA extends Catch_MA {
    public Grapple_MA(){
        super();
    }

    Double period(){
        return 4.0;
    }

    String name(){
        return "Grapples you";
    }

    double duration(){
        return (float)(Gamestate.w.randint(100,200));
    }

    String get_description(){
        return "One of your front row characters is caught, cannot move away, and will take damage steadily until you kill this monster.";
    }
}

class Finishing_Blow_MA extends Monster_action {
    public Finishing_Blow_MA(){
        super();
    }

    String name(){
        return "Finishing blow!";
    }

    String warning_message(){
        return "Prepares to strike a helpless character";
    }

    boolean finishing(){
        return true;
    }

    Double period(){
        return 4.0;
    }

    Double cooldown(){
        return 8.0;
    }

    Double delay(){
        return 2.0;
    }

    Double gc(){
        return 3.0;
    }

    boolean back_row_ok(){
        return false;
    }

    boolean ok(){
        for (Character c : Gamestate.p.chars)
            if (c.alive && !c.about_to_die && c.front_row())
                if (c.has_status_effect(Status_effect_code.EF_GRAPPLE) || c.has_status_effect(Status_effect_code.EF_STUN) || c.has_status_effect(Status_effect_code.EF_SLEEP))
                    return super.ok();
        return false;
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_DEATHBLOW){
    	    	Gamestate.add_sound("block");
                c.current_stance = null;
                return true;
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_MANY){
                c.current_stance = null;
                if (Gamestate.w.randint(1,100) <= Math.min(70,success_chance(12-m.level))){
                    return true;
                } else {
                    Gamestate.add_message("Distraction failed",c.image,Color.WHITE);
                }
            }
        }
        return false;
    }

    void make_stuff_happen(){
    	ArrayList<Character> choices = new ArrayList<Character>();
        for (Character c : Gamestate.p.chars)
            if (c.alive && !c.about_to_die && c.front_row())
                if (c.has_status_effect(Status_effect_code.EF_GRAPPLE) || c.has_status_effect(Status_effect_code.EF_STUN) || c.has_status_effect(Status_effect_code.EF_SLEEP))
                    choices.add(c);
        if (choices.size()>0){
            Character target = choices.get(Gamestate.w.randint(0,choices.size()-1));;
            Gamestate.add_sound("kill");
            int damage = Gamestate.w.randint(40,60);
            target.take_damage(damage-target.current_armor());
        }
    }

    String get_description(){
        return "Mortally wounds - or kills outright - a grappled, stunned, sleeping, constricted or hooked character. Can be countered with the 'Protect the Fallen' ability.";
    }
}

class Stun_MA extends Smite_MA {
    public Stun_MA(){
        super();
    }

    String name(){
        return "Stunning Blow";
    }

    String warning_message(){
        return "Menaces you";
    }

    Double delay(){
        return 1.5;
    }

    Double gc(){
        return delay() + 1;
    }

    Double period(){
        return 10.0;
    }

    int min_damage(){
        return 10;
    }

    int max_damage(){
        return 20;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("smite");
        int damage = Gamestate.w.randint(min_damage(),max_damage());
        char_target.take_damage(Math.max(0,damage-char_target.current_armor()));
        if (char_target.has_status_effect(Status_effect_code.EF_MEDITATION))
            return;
		for (Character_action a : char_target.actions)
			if (a.action_code==Character_action_code.CA_STUN_RESISTANCE)
				return;
		char_target.status_effects.put(Status_effect_code.EF_STUN,new Effect_parameters(duration()));
        char_target.action_in_progress = null;
        char_target.current_stance = null;
    }

    double duration(){
        return Gamestate.w.randint(6,12);
    }

    String get_description(){
        return "When it menaces you, tell one of your front-row characters to use the Block ability or you will get stunned.";
    }
}

class Squeeze_nasty_MA extends Squeeze_MA {
    public Squeeze_nasty_MA(){
        super();
    }

    Double period(){
        return 0.3;
    }

    Double cooldown(){
        return 0.2;
    }
}

class Squeeze_MA extends Monster_action {
    public Squeeze_MA(){
        super();
    }

    Double period(){
        return 0.7;
    }

    Double cooldown(){
        return 0.5;
    }

    boolean back_row_ok(){
        return false;
    }

    boolean ok(){
        for (Character c : Gamestate.p.chars)
            if (c.alive && !c.about_to_die && c.front_row())
                if (c.has_status_effect(Status_effect_code.EF_GRAPPLE) && c.grappled_by==m)
                    return super.ok();
        return false;
    }

    void make_stuff_happen(){
    	ArrayList<Character> choices = new ArrayList<Character>();
        for (Character c : Gamestate.p.chars)
            if (c.alive && !c.about_to_die && c.front_row())
                if (c.has_status_effect(Status_effect_code.EF_GRAPPLE) && c.grappled_by==m)
                    choices.add(c);
        if (choices.size()>0){
            Character target = choices.get(Gamestate.w.randint(0,choices.size()-1));;
            target.take_damage(2);
        }
    }
}

class Spell_Deflect_Missiles_MA extends Monster_action {
    public Spell_Deflect_Missiles_MA(){
        super();
    }

    String name(){
        return "Deflect Missiles";
    }

    Double period(){
        return 0.4;
    }

    boolean ok(){
        if (m.has_been_shot && m.ranged_only_evasion==0){
            return super.ok();
        } else {
            return false;
        }
    }

    void make_stuff_happen(){
        m.ranged_only_evasion = 6;
    }

    String get_description(){
        return "Makes the caster harder to hit with ranged attacks. The 'Mage Bane' ability is helpful here.";
    }
}

class Monster_Spell_MA extends Monster_action {
    public Monster_Spell_MA(){
        super();
    }

    boolean disruptable(){
        return true;
    }

    Double delay(){
        return 2.5;
    }

    Double gc(){
        return delay();
    }

    String warning_message(){
        return "Begins to cast " + name();
    }

    Double period(){
        return 8.0;
    }

    Double cooldown(){
        return 3.0;
    }
}

class Spell_Cause_Wounds_MA extends Monster_Spell_MA {
    public Spell_Cause_Wounds_MA(){
        super();
    }

    String name(){
        return "Cause Wounds";
    }

    boolean target_enemy(){
        return true;
    }

    int min_damage(){
        return 6;
    }

    int max_damage(){
        return 12;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("bad_spell");
        int damage = Gamestate.w.randint(min_damage(),max_damage());
        char_target.take_damage(damage);
    }

    String get_description(){
        return "Harms one of your characters. There is no defense.";
    }
}

class Spell_Firebolt_MA extends Monster_Spell_MA {
    public Spell_Firebolt_MA(){
        super();
    }

    String name(){
        return "Firebolt";
    }

    boolean bolt(){
        return true;
    }

    boolean fiery(){
        return true;
    }

    boolean target_enemy(){
        return true;
    }
//	    def enemy_target_back_row_ok(){
//	        return false;

    int min_damage(){
        return 10;
    }

    int max_damage(){
        return 20;
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_BOLTS){
    	    	Gamestate.add_sound("fail");
                m.countered_at = Gamestate.w.clock;
                c.current_stance = null;
                return true;
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_MANY){
                c.current_stance = null;
                if (Gamestate.w.randint(1,100) <= Math.min(70,success_chance(12-m.level))){
                    return true;
                } else {
                    Gamestate.add_message("Distraction failed",c.image,Color.WHITE);
                }
            }
        }
        return false;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("fire_bolt");
        int damage = Gamestate.w.randint(min_damage(),max_damage());
        if (char_target.has_status_effect(Status_effect_code.EF_IMMUNE_FIRE))
            damage = damage / 4;
        else if (char_target.has_status_effect(Status_effect_code.EF_RESIST_FIRE) || char_target.fire_resistance)
            damage = damage / 2;
        char_target.take_damage(damage);
        char_target.flash_image = "fire_down";
        char_target.flash_long = true;
    }

    String get_description(){
        return "Burns one of your characters. Armour is no protection, but fire resistance halves damage. Can be countered with the 'Reflection' spell.";
    }
}

class Mass_healing_MA extends Monster_Spell_MA {
    public Mass_healing_MA(){
        super();
    }

    String name(){
        return "Mass Healing";
    }

    boolean ok(){
        if (super.ok())
            for (Mob mm : Gamestate.e.mobs)
                if (mm.alive && !mm.about_to_die && mm.health < (mm.max_health - 10))
                    return true;
        return false;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("healing");
        for (Mob target : Gamestate.e.mobs){
            if (target!=m && target.alive && !target.about_to_die && target.health < (target.max_health - 10)){
	            int old_health = target.health;
	            target.health = Math.min(target.max_health,target.health + Gamestate.w.randint(15,25));
	            target.flash_number = target.health - old_health;
	            target.number_starts = Gamestate.w.clock;
	            target.number_color = Color.GREEN;
            }
        }
    }

    String get_description(){
        return "Heals all injured monsters on the screen (except the caster).";
    }
}

class Spell_Heal_MA extends Monster_Spell_MA {
    public Spell_Heal_MA(){
        super();
    }

    String name(){
        return "Healing";
    }

    boolean ok(){
        if (super.ok())
            for (Mob mm : Gamestate.e.mobs)
                if (mm.alive && !mm.about_to_die && mm.health < (mm.max_health - 10))
                    return true;
        return false;
    }

    void make_stuff_happen(){
    	ArrayList<Mob> choices = new ArrayList<Mob>();
        for (Mob mm : Gamestate.e.mobs)
            if (mm.alive && !mm.about_to_die && mm.health < (mm.max_health - 10))
                choices.add(mm);
        if (choices.size()>0){
            Mob target = choices.get(Gamestate.w.randint(0,choices.size()-1));;
            Gamestate.add_sound("healing");
            int old_health = target.health;
            target.health = Math.min(target.max_health,target.health + Gamestate.w.randint(15,25));
            target.flash_number = target.health - old_health;
            target.number_starts = Gamestate.w.clock;
            target.number_color = Color.GREEN;
        }
    }

    String get_description(){
        return "Heals an injured monster.";
    }
}

class Breath_lightning_MA extends Breath_mini_lightning_MA {
    public Breath_lightning_MA(){
        super();
    }

    Double delay(){
        return 0.0;
    }

    boolean back_row_ok(){
        return true;
    }

    boolean front_row_ok(){
        return false;
    }

    Double period(){
        return 3.0;
    }

    Integer n_uses(){
        return 0;
    }

    Double cooldown(){
        return 8.0;
    }

    int damroll(){
        return Gamestate.w.randint(100,200);
    }

    String get_description(){
        return "Zaps one of your characters. Can be countered with the 'Reflection' spell, or the 'Take Cover' ability. There will be no warning!";
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_BOLTS){
            	Gamestate.add_message("Prepares to breathe!",m.image,Color.RED);
                break;
            }
        }
        return super.prevented();
    }
}

class Breath_small_lightning_MA extends Breath_mini_lightning_MA {
    public Breath_small_lightning_MA(){
        super();
    }

    Double delay(){
        return 1.5;
    }

    Double cooldown(){
        return 3.0;
    }

    boolean enemy_target_back_row_ok(){
        return true;
    }

    int damroll(){
        return Gamestate.w.randint(30,60);
    }
}

class Breath_mini_lightning_MA extends Monster_action {
    public Breath_mini_lightning_MA(){
        super();
    }

    String name(){
        return "Breathes lightning!!";
    }

    String warning_message(){
        return "Inhales...";
    }

    Double delay(){
        return 2.0;
    }

    Double period(){
        return 6.0;
    }

    Double likelihood_increase_until_n_uses(){
        return 3.0;
    }

    Integer n_uses(){
        return 2;
    }

    Double cooldown(){
        return 5.0;
    }

    Double gc(){
        return 2.0;
    }

    boolean back_row_ok(){
        return false;
    }

    boolean target_enemy(){
        return true;
    }

    boolean enemy_target_back_row_ok(){
        return false;
    }

    boolean take_coverable(){
        return true;
    }

    boolean bolt(){
        return true;
    }

    int damroll(){
        return Gamestate.w.randint(20,40);
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_BOLTS){
    	    	Gamestate.add_sound("fail");
                m.countered_at = Gamestate.w.clock;
                c.current_stance = null;
                return true;
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_MANY){
                c.current_stance = null;
                if (Gamestate.w.randint(1,100) <= Math.min(70,success_chance(12-m.level))){
                    return true;
                } else {
                    Gamestate.add_message("Distraction failed",c.image,Color.WHITE);
                }
            }
        }
        return false;
    }

    void make_stuff_happen(){
        if (char_target.current_stance==Stance_code.ST_TAKE_COVER){
			Gamestate.add_sound("monmiss");
			return;
        }
        Gamestate.add_sound("lightning_bolt");
        int damage = damroll();
        if (char_target.lightning_resistance) damage = damage/2;
        char_target.take_damage(damage);
    	char_target.flash_image = "lightning_down";
    	char_target.flash_long = true;
    }

    String get_description(){
        return "Zaps one of your characters. Armour is no protection. Can be countered with the 'Reflection' spell, or the 'Take Cover' ability (though this is not reliable).";
    }
}

class Breath_mini_cold_MA extends Breath_mini_lightning_MA {
    public Breath_mini_cold_MA(){
        super();
    }

    String name(){
        return "Breathes cold!!";
    }

    Double delay(){
        return 2.5;
    }

    boolean bolt(){
        return false;
    }

    boolean freezy(){
        return true;
    }

    boolean prevented(){
        return false;
    }

    int damroll(){
        return Gamestate.w.randint(20,30);
    }

    void make_stuff_happen(){
        Gamestate.add_sound("wind");
        ArrayList<Character> targets = new ArrayList<Character>();
        Character t1 = Gamestate.p.random_front_target(m);
        if (t1==null) return;
        targets.add(t1);
        Character t2 = null;
        if (t1.pos==Position_code.POS_C_FRONT_1) t2=Gamestate.p.get_char_at_pos(Position_code.POS_C_BACK_1);
        if (t1.pos==Position_code.POS_C_FRONT_2) t2=Gamestate.p.get_char_at_pos(Position_code.POS_C_BACK_2);
        if (t1.pos==Position_code.POS_C_FRONT_3) t2=Gamestate.p.get_char_at_pos(Position_code.POS_C_BACK_3);
        if (t2!=null) targets.add(t2);
        for (Character c : targets){
            if (c.alive && !c.about_to_die){
                int damage = damroll();
                if (c.current_stance==Stance_code.ST_TAKE_COVER){
                    damage = damage / 3;
                    c.current_stance = null;
                } else {
             	   if (c.has_status_effect(Status_effect_code.EF_RESIST_COLD) || c.cold_resistance)
                        damage = damage / 2;
                }
                c.take_damage(damage);
            	c.flash_image = "cold_down";
            	c.flash_long = true;
            }
        }
     }

    String get_description(){
        return "Damages two of your characters with cold. Armour is no protection. Can be countered with the 'Take Cover' ability (though this is not reliable).";
    }
}

class Spell_Lightning_MA extends Monster_Spell_MA {
    public Spell_Lightning_MA(){
        super();
    }

    String name(){
        return "Lightning Bolt";
    }

    boolean bolt(){
        return true;
    }

    boolean target_enemy(){
        return true;
    }
 //   def enemy_target_back_row_ok(){
 //       return false;

    int min_damage(){
        return 25;
    }

    int max_damage(){
        return 50;
    }

    boolean take_coverable(){
        return true;
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_BOLTS){
    	    	Gamestate.add_sound("fail");
                m.countered_at = Gamestate.w.clock;
                c.current_stance = null;
                return true;
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_MANY){
                c.current_stance = null;
                if (Gamestate.w.randint(1,100) <= Math.min(70,success_chance(12-m.level))){
                    return true;
                } else {
                    Gamestate.add_message("Distraction failed",c.image,Color.WHITE);
                }
            }
        }
        return false;
    }

    void make_stuff_happen(){
        if (char_target.current_stance==Stance_code.ST_TAKE_COVER){
			Gamestate.add_sound("monmiss");
			return;
        }
        Gamestate.add_sound("lightning_bolt");
        int damage = Gamestate.w.randint(min_damage(),max_damage());
        if (char_target.lightning_resistance) damage = damage/2;
        char_target.take_damage(damage);
    	char_target.flash_image = "lightning_down";
    	char_target.flash_long = true;
    }

    String get_description(){
        return "Zaps one of your characters. Armour is no protection. Can be countered with the 'Reflection' spell, or the 'Take Cover' ability (though this is not reliable).";
    }
}

class Spell_Chill_Dart_MA extends Spell_Lightning_MA {
    public Spell_Chill_Dart_MA(){
        super();
    }

    String name(){
        return "Chill Dart";
    }

    Double delay(){
        return 1.0;
    }

    boolean back_row(){
        return false;
    }

    boolean enemy_target_back_row_ok(){
        return true;
    }

    boolean enemy_target_front_row_ok(){
        return false;
    }

    boolean take_coverable(){
        return false;
    }

    boolean freezy(){
        return true;
    }

    int min_damage(){
        return 7;
    }

    int max_damage(){
        return 14;
    }

    double duration(){
        return Gamestate.w.randint(1,2);
    }

    void make_stuff_happen(){
        Gamestate.add_sound("ice_bolt");
        int damage = Gamestate.w.randint(min_damage(),max_damage());
        if (char_target.has_status_effect(Status_effect_code.EF_RESIST_COLD) || char_target.cold_resistance)
            damage = damage / 2;
        char_target.take_damage(damage);
        if (char_target.alive && !char_target.about_to_die && !(char_target.has_status_effect(Status_effect_code.EF_RESIST_COLD)) && !char_target.cold_resistance){
        	char_target.status_effects.put(Status_effect_code.EF_PARALYSIS,new Effect_parameters(duration()));
        	char_target.action_in_progress = null;
        	char_target.current_stance = null;
        	char_target.flash_image = "cold_down";
        	char_target.flash_long = true;
        }
    }

    String get_description(){
        return "Zaps one of your back row characters, from the enemy front row. Armour is no protection. Can be countered with the 'Reflection' spell, though you have to be very quick. Cold resistance also helps.";
    }
}

class Spell_Shadow_Bolt_MA extends Spell_Lightning_MA {
    public Spell_Shadow_Bolt_MA(){
        super();
    }

    String name(){
        return "Shadow Bolt";
    }

    boolean front_row(){
        return false;
    }

    boolean enemy_target_back_row_ok(){
        return true;
    }

    boolean enemy_target_front_row_ok(){
        return false;
    }

    boolean prevented(){
    	if (super.prevented()) return true;
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_DARKNESS){
                m.countered_at = Gamestate.w.clock;
                c.current_stance = null;
                return true;
            }
        }
        return false;
    }

    Double delay(){
        return 1.5;
    }

    boolean take_coverable(){
        return false;
    }

    int min_damage(){
        return 5;
    }

    int max_damage(){
        return 15;
    }

    double duration(){
        return Gamestate.w.randint(1,2);
    }

    void make_stuff_happen(){
        Gamestate.add_sound("dark");
        int damage = Gamestate.w.randint(min_damage(),max_damage());
        char_target.take_damage(damage);
        if (char_target.alive && !char_target.about_to_die){
			for (Character_action a : char_target.actions)
				if (a.action_code==Character_action_code.CA_STUN_RESISTANCE)
					return;
			char_target.status_effects.put(Status_effect_code.EF_STUN,new Effect_parameters(duration()));
        	char_target.action_in_progress = null;
        	char_target.current_stance = null;
        	char_target.flash_image = "shadowbolt_down";
        	char_target.flash_long = true;
        }
    }

    boolean dark(){
    	return true;
    }

    String get_description(){
        return "Zaps one of your back row characters. Armour is no protection. Can be countered with the 'Reflection' or 'Light' spells.";
    }
}

class Spell_Paralysis_MA extends Monster_Spell_MA {
    public Spell_Paralysis_MA(){
        super();
    }

    String name(){
        return "Paralysis";
    }

    boolean target_enemy(){
        return true;
    }

    boolean paralysing(){
        return true;
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_PARALYSIS){
    	    	Gamestate.add_sound("fail");
                m.countered_at = Gamestate.w.clock;
                c.current_stance = null;
                return true;
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_MANY){
                c.current_stance = null;
                if (Gamestate.w.randint(1,100) <= Math.min(70,success_chance(12-m.level))){
                    return true;
                } else {
                    Gamestate.add_message("Distraction failed",c.image,Color.WHITE);
                }
            }
        }
        return false;
    }

    double duration(){
        return Gamestate.w.randint(12,20);
    }

    void make_stuff_happen(){
        if (char_target.alive && !char_target.about_to_die){
            boolean immune = false;
            for (Character_action a : char_target.actions){
                if (a.action_code==Character_action_code.CA_FREE_ACTION){
                    immune = true;
                    continue;
                }
            }
            if (char_target.has_status_effect(Status_effect_code.EF_MEDITATION))
                immune = true;
            if (!immune){
    	        Gamestate.add_sound("grunt");
            	char_target.status_effects.put(Status_effect_code.EF_PARALYSIS,new Effect_parameters(duration()));
            	char_target.action_in_progress = null;
            	char_target.current_stance = null;
            }
        }
    }

    String get_description(){
        return "Paralyses one of your characters. Can be countered with the 'Counter: Paralysis' spell.";
    }
}

class Spell_Scare_MA extends Monster_Spell_MA {
    public Spell_Scare_MA(){
        super();
    }

    String name(){
        return "Cause Fear";
    }

    boolean target_enemy(){
        return true;
    }

    double duration(){
        return Gamestate.w.randint(8,12);
    }

    boolean scary(){
        return true;
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_FEAR){
    	    	Gamestate.add_sound("fail");
                m.countered_at = Gamestate.w.clock;
                c.current_stance = null;
                return true;
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_MANY){
                c.current_stance = null;
                if (Gamestate.w.randint(1,100) <= Math.min(70,success_chance(12-m.level))){
                    return true;
                } else {
                    Gamestate.add_message("Distraction failed",c.image,Color.WHITE);
                }
            }
        }
        return false;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("bad_spell");
    	if (Gamestate.p.fear_resistant()) return;
        if (char_target.alive && !char_target.about_to_die){
            boolean immune = false;
            for (Character_action a : char_target.actions){
                if (a.action_code==Character_action_code.CA_RESIST_FEAR){
                    immune = true;
                    continue;
                }
            }
            if (char_target.has_status_effect(Status_effect_code.EF_MEDITATION))
                immune = true;
            if (!immune){
            	char_target.status_effects.put(Status_effect_code.EF_FEAR,new Effect_parameters(duration()));
            	char_target.action_in_progress = null;
            	char_target.current_stance = null;
            }
        }
    }

    String get_description(){
        return "Makes one of your characters afraid. Can be countered by the 'Counter: Fear' spell.";
    }
}

class Spell_Silence_MA extends Monster_Spell_MA {
    public Spell_Silence_MA(){
        super();
    }

    String name(){
        return "Silence";
    }

    boolean silencing(){
        return true;
    }

    Double delay(){
        return 2.0;
    }

    void make_stuff_happen(){
       Gamestate.w.silence_until = Gamestate.w.clock + Gamestate.w.randint(6,12);
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_SILENCE){
    	    	Gamestate.add_sound("fail");
                m.countered_at = Gamestate.w.clock;
                c.current_stance = null;
                return true;
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_MANY){
                c.current_stance = null;
                if (Gamestate.w.randint(1,100) <= Math.min(70,success_chance(12-m.level))){
                    return true;
                } else {
                    Gamestate.add_message("Distraction failed",c.image,Color.WHITE);
                }
            }
        }
        return false;
    }

    String get_description(){
        return "Creates a magical silence, preventing your characters from casting any spells. Can be countered by the 'Counter: Silence' spell.";
    }
}

class Spell_Sleep_MA extends Monster_Spell_MA {
    public Spell_Sleep_MA(){
        super();
    }

    String name(){
        return "Sleep";
    }

    boolean sleepy(){
        return true;
    }

    boolean target_enemy(){
        return true;
    }

    double duration(){
        return Gamestate.w.randint(10,15);
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_SLEEP){
    	    	Gamestate.add_sound("fail");
                m.countered_at = Gamestate.w.clock;
                c.current_stance = null;
                return true;
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_MANY){
                c.current_stance = null;
                if (Gamestate.w.randint(1,100) <= Math.min(70,success_chance(12-m.level))){
                    return true;
                } else {
                    Gamestate.add_message("Distraction failed",c.image,Color.WHITE);
                }
            }
        }
        return false;
    }

    void make_stuff_happen(){
        if (char_target.alive && !char_target.about_to_die){
            if (char_target.has_status_effect(Status_effect_code.EF_MEDITATION))
                return;
            if (char_target.sleep_resistance)
            	return;
	        Gamestate.add_sound("yawn");
            char_target.status_effects.put(Status_effect_code.EF_SLEEP,new Effect_parameters(duration()));
            char_target.status_effects.get(Status_effect_code.EF_SLEEP).level = 3;
            char_target.action_in_progress = null;
            char_target.current_stance = null;
        }
    }

    String get_description(){
        return "Makes one of your characters fall asleep. Click on them three times to wake them up. Can be countered by the 'Counter: Sleep' spell.";
    }
}

class Get_Confused_MA extends Monster_action {
    public Get_Confused_MA(){
        super();
    }

    String name(){
        return "Gets confused";
    }

    Double period(){
        return 12.5;
    }

    Double cooldown(){
        return 6.0;
    }

    boolean ok(){
        if (!m.confused)
            return super.ok();
        return false;
    }

    double duration(){
        return Gamestate.w.randint(4,6);
    }

    void make_stuff_happen(){
        Gamestate.add_sound("confuse");
        m.confused = true;
        m.confused_until = Gamestate.w.clock + duration();
    }

    String get_description(){
        return "Wanders around aimlessly for a few seconds, and may hit other monsters nearby. A monster with this ability is particularly vulnerable to your confusion spells.";
    }
}

class Rally_Orcs_MA extends Monster_action {
    public Rally_Orcs_MA(){
        super();
    }

    String name(){
        return "Rally Orcs";
    }

    Double period(){
        return 5.0;
    }

    Double cooldown(){
        return 7.0;
    }

    boolean is_eligible(Mob mm){
        return (mm.code==Monster_code.MOB_ORC_WARRIOR || mm.code==Monster_code.MOB_ORC_WRETCH || mm.code==Monster_code.MOB_ORC_ROCK_THROWER || mm.code==Monster_code.MOB_ORC_WITH_MANCATCHER || mm.code==Monster_code.MOB_ORC_CHAMPION);
    }

    boolean ok(){
        for (Mob mm : Gamestate.e.mobs)
            if (mm.alive && !mm.about_to_die && m.id!=mm.id)
                if (is_eligible(mm) && mm.rallied_until <= Gamestate.w.clock)
                    return super.ok();
        return false;
    }

    double duration(){
        return Gamestate.w.randint(6,12);
    }

    void make_stuff_happen(){
        int n_rallied = 0;
        for (Mob mm : Gamestate.e.mobs){
            if (mm.alive && !mm.about_to_die && is_eligible(mm)){
                n_rallied = n_rallied + 1;
                mm.rallied_until = Gamestate.w.clock + duration();
            }
        }
        if (n_rallied>0)
            Gamestate.add_sound(sound());
    }

    String sound(){
    	return "rally";
    }

    String get_description(){
        return "Gives all the other orcs a temporary boost.";
    }
}

class Rally_Troglodytes_MA extends Rally_Orcs_MA {
    public Rally_Troglodytes_MA(){
        super();
    }

    String name(){
        return "Rally Troglodytes";
    }

    boolean is_eligible(Mob mm){
        return (mm.code==Monster_code.MOB_TROGLODYTE || mm.code==Monster_code.MOB_TROGLODYTE_ARCHER || mm.code==Monster_code.MOB_TROGLODYTE_SHAMAN || mm.code==Monster_code.MOB_TROGLODYTE_PRIEST);
    }

    String get_description(){
        return "Gives all the other troglodytes a temporary boost.";
    }

    String sound(){
    	return "trog rally";
    }
}

class Bite_MA extends Super_Smite_MA {
    public Bite_MA(){
        super();
    }
    String name(){
        return "Bite";
    }

    Double period(){
        return 5.0;
    }

    String warning_message(){
        return "Opens its jaws";
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_EPIC_BLOCK){
    	    	Gamestate.add_sound("block");
                c.current_stance = null;
                return true;
            } else if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_LEAP_ASIDE){
    	    	Gamestate.add_sound("whoosh");
                c.current_stance = null;
                return true;
            }
        }
        return false;
    }

    int min_damage(){
        return 40;
    }

    int max_damage(){
        return 80;
    }

    boolean leap_awayable(){
        return true;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("bite");
        int damage = Gamestate.w.randint(min_damage(),max_damage());
        int damage_taken = Math.max(0,damage-char_target.current_armor());
        char_target.take_damage(damage_taken);
    }

    String get_description(){
        return "When it opens its jaws, you had better Epic Block or Leap Aside.";
    }
}

class Super_smite_often_MA extends Super_Smite_MA {
    public Super_smite_often_MA(){
        super();
    }

    Double period(){
        return 4.0;
    }

    int min_damage(){
        return 30;
    }

    int max_damage(){
        return 60;
    }
}

class Super_Smite_MA extends Smite_MA {
    public Super_Smite_MA(){
        super();
    }

    String name(){
        return "Super Smite";
    }

    String warning_message(){
        return "'SMASH YOU!'";
    }

    Double delay(){
        return 2.0;
    }

    Double period(){
        return 8.0;
    }

    boolean blockable(){
        return false; // don"t try to block by reflex;
    }

    boolean epic_blockable(){
        return true;
    }

    boolean leap_awayable(){
        return true;
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_BLOCK){
                c.current_stance = null;
                if (Gamestate.w.one_in(2)){
        	    	Gamestate.add_sound("block");
                    return true;
                } else {
                    Gamestate.add_message("Failed to block",c.image,Color.WHITE);
                    return false;
                }
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_LEAP_ASIDE){
    	    	Gamestate.add_sound("whoosh");
                c.current_stance = null;
                return true;
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_MANY){
                c.current_stance = null;
                if (Gamestate.w.randint(1,100) <= Math.min(70,success_chance(12-m.level))){
                    return true;
                } else {
                    Gamestate.add_message("Distraction failed",c.image,Color.WHITE);
                }
            }
        }
        return false;
    }

    int min_damage(){
        return 20;
    }

    int max_damage(){
        return 40;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("smite");
        int damage = Gamestate.w.randint(min_damage(),max_damage());
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_EPIC_BLOCK){
    	    	Gamestate.add_sound("block");
            	Gamestate.add_message("Blocked!",c.image,Color.WHITE);
                damage = Math.max(1,damage/5);
                c.current_stance = null;
                break;
            }
        }
        int damage_taken = Math.max(0,damage-char_target.current_armor());
        char_target.take_damage(damage_taken);
    }

    String get_description(){
        return "When it threatens to smash you, you had better defend yourself. An ordinary Block may or may not work. An Epic Block will deflect most of the damage, or Leap Aside will protect you if you time it right.";
    }
}

class Cleave_MA extends Monster_action {
    public Cleave_MA(){
        super();
    }

    String name(){
        return "Cleave!";
    }

    String warning_message(){
        return "Begins a scything blow!";
    }

    Double delay(){
        return 2.0;
    }

    Double period(){
        return 8.0;
    }

    Double gc(){
        return delay()+1.0;
    }

    boolean back_row_ok(){
        return false;
    }

    boolean epic_blockable(){
        return true;
    }

    boolean leap_awayable(){
        return true;
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_EPIC_BLOCK){
    	    	Gamestate.add_sound("block");
                return true;
            }
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_MANY){
                c.current_stance = null;
                if (Gamestate.w.randint(1,100) <= Math.min(70,success_chance(12-m.level))){
                    return true;
                } else {
                    Gamestate.add_message("Distraction failed",c.image,Color.WHITE);
                }
            }
        }
        return false;
    }

    int min_damage(){
        return 10;
    }

    int max_damage(){
        return 20;
    }

    void make_stuff_happen(){
        int smitten = 0;
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.front_row()){
                if (c.current_stance!=null && c.current_stance==Stance_code.ST_BLOCK){
        	    	Gamestate.add_sound("block");
                    c.current_stance=null;
                } else if (c.current_stance!=null && c.current_stance==Stance_code.ST_LEAP_ASIDE){
        	    	Gamestate.add_sound("whoosh");
                    c.current_stance=null;
                } else {
                    if (m.melee_hit_roll(c)){
                        int damage = Gamestate.w.randint(min_damage(),max_damage());
                        int damage_taken = Math.max(0,damage-c.current_armor());
                        if (damage_taken > 0){
                            smitten = smitten + 1;
                            c.take_damage(damage_taken);
                        }
                    }
                }
            }
        }
        if (smitten>0){
            Gamestate.add_sound("cleave");
        } else {
            Gamestate.add_sound("monmiss");
        }
    }

    String get_description(){
        return "When it hefts its axe, you had better defend yourself. It will attack all three front-row characters. An Epic Block will save you: an ordinary Block or Leap Aside will save only one character.";
    }
}

class Charge_MA extends Super_Smite_MA {
    public Charge_MA(){
        super();
    }

    String name(){
        return "Charge";
    }

    String warning_message(){
        return "Lowers its horns";
    }

    Double delay(){
        return 1.2;
    }

    int min_damage(){
        return 12;
    }

    int max_damage(){
        return 24;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("minotaur");
        int damage = Gamestate.w.randint(min_damage(),max_damage());
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_EPIC_BLOCK){
    	    	Gamestate.add_sound("block");
            	Gamestate.add_message("Blocked!",c.image,Color.WHITE);
                damage = Math.max(1,damage/5);
                break;
            }
        }
        int damage_taken = Math.max(0,damage-char_target.current_armor());
        char_target.take_damage(damage_taken);
        if (char_target.alive && !char_target.about_to_die && damage_taken >= 5){
			for (Character_action a : char_target.actions)
				if (a.action_code==Character_action_code.CA_STUN_RESISTANCE)
					return;
        	char_target.status_effects.put(Status_effect_code.EF_STUN,new Effect_parameters(Math.min(damage_taken,Gamestate.w.randint(3,6))));
    	}
    }

    String get_description(){
        return "When it lowers its horns, you had better defend yourself. An ordinary Block may or may not work. An Epic Block will deflect most of the damage, or Leap Aside will protect you if you time it right.";
    }
}

class Steal_MA extends Monster_action {
    public Steal_MA(){
        super();
    }

    String name(){
        return "Steal";
    }
    
    String message(){
        return "";
    }

    Double period(){
        return 1.5;
    }

    Double cooldown(){
        return 5.0;
    }

    boolean back_row_ok(){
        return false;
    }

    boolean ok(){
        if (!super.ok())
            return false;
        if (Gamestate.w.collected_gold<=0)
            return false;
        for (Character c : Gamestate.p.chars)
            if (c.alive && !c.about_to_die && c.front_row() && (c.has_status_effect(Status_effect_code.EF_STUN) || c.has_status_effect(Status_effect_code.EF_SLEEP) || c.has_status_effect(Status_effect_code.EF_PARALYSIS) || c.has_status_effect(Status_effect_code.EF_GRAPPLE)))
                return true;
        return false;
    }

    void make_stuff_happen(){
        if (Gamestate.w.collected_gold<=0)
            return;
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.front_row() && (c.has_status_effect(Status_effect_code.EF_STUN) || c.has_status_effect(Status_effect_code.EF_SLEEP) || c.has_status_effect(Status_effect_code.EF_PARALYSIS) || c.has_status_effect(Status_effect_code.EF_GRAPPLE))){
                Gamestate.add_sound("bad_spell");
                int gold_stolen = Math.max(0,Math.min(Gamestate.w.collected_gold,Math.max(Gamestate.w.randint(10,40),(Gamestate.w.randint((int)(Math.floor(0.1*Gamestate.w.collected_gold)),(int)(Math.floor(0.3*Gamestate.w.collected_gold)))))));
                Gamestate.w.collected_gold = Gamestate.w.collected_gold - gold_stolen;
                m.stolen_gold = m.stolen_gold + gold_stolen;
                Gamestate.add_message("Stolen " + (gold_stolen) + " gold from " + c.name + "!",m.image,Color.WHITE);
                return;
            }
        }
    }

    String get_description(){
        return "He can steal your money if you are distracted.";
    }
}

class Breath_Poison_Cloud_MA extends Monster_action {
    public Breath_Poison_Cloud_MA(){
        super();
    }

    String name(){
        return "Breathes poison!!";
    }

    Double period(){
        return 3.0;
    }

    Double cooldown(){
        return 12.0;
    }

    Double gc(){
        return 3.0;
    }

    boolean back_row_ok(){
        return false;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("gas");
        Gamestate.w.poison_cloud_until = Gamestate.w.clock + 8.0;
        Gamestate.w.poison_cloud_from = m;
    }

    String get_description(){
        return "Breathes out a great cloud of poison on your front row characters.";
    }
}

class Spell_Poison_Cloud_MA extends Monster_Spell_MA {
    public Spell_Poison_Cloud_MA(){
        super();
    }

    String name(){
        return "Poison Cloud";
    }

    Double cooldown(){
        return 25.0;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("gas");
        Gamestate.w.poison_cloud_until = Gamestate.w.clock + 12.0;
        Gamestate.w.poison_cloud_from = m;
    }

    String get_description(){
        return "Creates a poisonous cloud enveloping your entire front row, which will last for some time. You had better not stand in it if you can help it, especially if you don't have poison resistance. If you kill the caster the poison will dissipate harmlessly.";
    }
}

class Spell_Invulnerability_MA extends Monster_Spell_MA {
    public Spell_Invulnerability_MA(){
        super();
    }

    String name(){
        return "Invulnerability";
    }

    Double cooldown(){
        return 12.0;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("invulnerable");
        m.completely_invulnerable_until = Gamestate.w.clock + 8.0;
    }

    String get_description(){
        return "Makes the caster completely invulnerable to all physical damage for a few seconds.";
    }
}

class Finger_of_death_MA extends Monster_action {
    public Finger_of_death_MA(){
        super();
    }

    String name(){
        return "Finger of Death";
    }

    String message(){
        return "Die!";
    }

    boolean disruptable(){
        return false;
    }

    Double delay(){
        return 2.5;
    }

    Double gc(){
        return delay();
    }

    String warning_message(){
        return "'Hmmm... The Finger of Death, I think...'";
    }

    Double period(){
        return 2.0;
    }

    Double cooldown(){
        return 60.0;
    }

	boolean target_enemy(){
		return true;
	}

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_CHALICE){
                c.current_stance = null;
                return true;
            }
        }
        return false;
    }

    void make_stuff_happen(){
        Gamestate.add_sound("death magic");
        char_target.die();
    }

    String get_description(){
        return "Kills the unfortunate target instantly. Only one defense is known - the priest spell 'Chalice of Incorruptible Life'.";
    }
}

class Darkness_storm_MA extends Monster_action {
    public Darkness_storm_MA(){
        super();
    }

    String name(){
        return "Darkness Storm";
    }

    String message(){
        return "Invokes a storm of darkness!";
    }

    Double period(){
        return 3.0;
    }

    Double cooldown(){
        return 5.0;
    }

    int damroll(){
    	return Gamestate.w.randint(5, 15);
    }

    void make_stuff_happen(){
        Gamestate.add_sound("dark");
        Gamestate.w.darkness_at = Gamestate.w.clock;
        if (Gamestate.w.darkness<3)
        	Gamestate.w.darkness=3;
    	else
    		Gamestate.w.darkness=Gamestate.w.darkness+1;
        for (Character c : Gamestate.p.chars){
        	if (c.alive && !c.about_to_die){
        		c.take_damage(damroll());
        	}
        }
        Gamestate.w.dark_flare = Gamestate.w.clock + 0.5;
    }

    String get_description(){
        return "Causes shadow damage to the entire party. There is no defense, although the Light spell can be used to clear the ensuing darkness.";
    }
}

class Big_shadow_bolt_MA extends Spell_Shadow_Bolt_MA {
    public Big_shadow_bolt_MA(){
        super();
    }

    boolean front_row(){
        return true;
    }

    int min_damage(){
        return 15;
    }

    int max_damage(){
        return 25;
    }

    double duration(){
        return Gamestate.w.randint(2,4);
    }
}

class Draining_touch_MA extends Monster_action {
    public Draining_touch_MA(){
        super();
    }

    String name(){
        return "Leech";
    }

    Double period(){
        return 2.0;
    }

    Double cooldown(){
        return 3.0;
    }

    boolean back_row_ok(){
        return false;
    }

    boolean target_enemy(){
        return true;
    }

    boolean enemy_target_front_row_ok(){
        return true;
    }

    boolean enemy_target_back_row_ok(){
        return false;
    }

    boolean prevented(){
        for (Character c : Gamestate.p.chars){
            if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_COUNTER_DRAINING){
    	    	Gamestate.add_sound("fail");
                c.current_stance = null;
                return true;
            }
        }
        return false;
    }

    int damroll(){
    	return Gamestate.w.randint(10,20);
    }

    void make_stuff_happen(){
    	if (!char_target.draining_resistance){
	        Gamestate.add_sound("exhale");
	        int damage = damroll();
	        if (damage > char_target.health){
	        	damage = char_target.health;
	        }
	        char_target.take_damage(damroll());
	        m.health = (int)Math.min(m.max_health,m.health+damage*2);
    	} else {
	        Gamestate.add_sound("cancel");
            Gamestate.add_message("Resists!",char_target.image,Color.WHITE);
    	}
    }

    String get_description(){
        return "This evil spell will drain your character's green bar, and at the same time heal the caster.";
    }
}

